J'ai besoin de créer une fonction qui ne peut être exécutée qu'une seule fois. Après la première, elle ne sera pas exécutée. Je connais C++ et Java à propos des variables statiques qui peuvent faire le travail, mais j'aimerais savoir s'il existe un moyen plus élégant de le faire?
Si par "ne sera pas exécuté" vous voulez dire "ne fera rien s'il est appelé plus d'une fois", vous pouvez créer une fermeture:
var something = (function() {
var executed = false;
return function() {
if (!executed) {
executed = true;
// do something
}
};
})();
something(); // "do something" happens
something(); // nothing happens
En réponse à un commentaire de @Vladloffe (maintenant supprimé): avec une variable globale, un autre code pourrait réinitialiser la valeur de l'indicateur "exécuté" (quel que soit le nom que vous lui donniez). Avec une fermeture, aucun autre code n’a aucun moyen de le faire, que ce soit accidentellement ou délibérément.
Comme d'autres réponses le soulignent, plusieurs bibliothèques (telles que nderscore et Ramda ) ont une petite fonction utilitaire (généralement appelée once()
[*]) qui accepte une fonction en tant qu'argument et renvoie une autre fonction qui appelle la fonction fournie une seule fois, quel que soit le nombre d'appels de la fonction renvoyée. La fonction renvoyée met également en cache la valeur renvoyée en premier par la fonction fournie et la renvoie lors d'appels suivants.
Toutefois, si vous n'utilisez pas une telle bibliothèque tierce, mais souhaitez tout de même une telle fonction utilitaire (plutôt que la solution de nonce que j'ai proposée ci-dessus), sa mise en œuvre est assez simple. La plus belle version que j'ai vue est celle-ci publiée par David Walsh :
function once(fn, context) {
var result;
return function() {
if (fn) {
result = fn.apply(context || this, arguments);
fn = null;
}
return result;
};
}
Je serais enclin à changer fn = null;
en fn = context = null;
. Il n’ya aucune raison pour que la fermeture maintienne une référence à context
une fois que fn
a été appelé.
[*]Sachez cependant que d'autres bibliothèques, telles que this Drupal extension à jQuery , peuvent avoir une fonction nommée once()
qui fait quelque chose de très différent.
Remplacez-le par un NOOP réutilisable (aucune opération) fonction.
// this function does nothing
function noop() {};
function foo() {
foo = noop; // swap the functions
// do your thing
}
function bar() {
bar = noop; // swap the functions
// do your thing
}
function myFunc(){
myFunc = function(){}; // kill it as soon as it was called
console.log('call once and never again!'); // your stuff here
};
<button onClick=myFunc()>Call myFunc()</button>
var myFunc = function func(){
if( myFunc.fired ) return;
myFunc.fired = true;
console.log('called once and never again!'); // your stuff here
};
// even if referenced & "renamed"
((refToMyfunc)=>{
setInterval(refToMyfunc, 1000);
})(myFunc)
UnderscoreJs a une fonction qui fait cela, nderscorejs.org/#once
// Returns a function that will be executed at most one time, no matter how
// often you call it. Useful for lazy initialization.
_.once = function(func) {
var ran = false, memo;
return function() {
if (ran) return memo;
ran = true;
memo = func.apply(this, arguments);
func = null;
return memo;
};
};
En parlant de variables statiques, ceci ressemble un peu à la variante de fermeture:
var once = function() {
if(once.done) return;
console.log('Doing this once!');
once.done = true;
};
once(); once();
Vous pouvez ensuite réinitialiser une fonction si vous le souhaitez:
once.done = false;
var quit = false;
function something() {
if(quit) {
return;
}
quit = true;
... other code....
}
Vous pourriez simplement avoir la fonction "se retirer"
function Once(){
console.log("run");
Once = undefined;
}
Once(); // run
Once(); // Uncaught TypeError: undefined is not a function
Mais cela peut ne pas être la meilleure réponse si vous ne voulez pas avaler des erreurs.
Vous pouvez aussi faire ceci:
function Once(){
console.log("run");
Once = function(){};
}
Once(); // run
Once(); // nothing happens
J'ai besoin que cela fonctionne comme un pointeur intelligent, si aucun élément de type A ne peut être exécuté, s'il y a un ou plusieurs éléments A, la fonction ne peut pas être exécutée.
function Conditional(){
if (!<no elements from type A>) return;
// do stuff
}
essaye ça
var fun = (function() {
var called = false;
return function() {
if (!called) {
console.log("I called");
called = true;
}
}
})()
D'un mec nommé Crockford ... :)
function once(func) {
return function () {
var f = func;
func = null;
return f.apply(
this,
arguments
);
};
}
Si vous voulez être en mesure de réutiliser la fonction dans le futur, alors cela fonctionne bien, basé sur le code d'Ed Hopp ci-dessus (je réalise que la question initiale ne demandait pas cette fonctionnalité supplémentaire!):
var something = (function() {
var executed = false;
return function(value) {
// if an argument is not present then
if(arguments.length == 0) {
if (!executed) {
executed = true;
//Do stuff here only once unless reset
console.log("Hello World!");
}
else return;
} else {
// otherwise allow the function to fire again
executed = value;
return;
}
}
})();
something();//Hello World!
something();
something();
console.log("Reset"); //Reset
something(false);
something();//Hello World!
something();
something();
La sortie ressemble à:
Hello World!
Reset
Hello World!
Si vous utilisez Node.js ou écrivez JavaScript avec browserify, considérez le module "une fois" npm :
var once = require('once')
function load (file, cb) {
cb = once(cb)
loader.load('file')
loader.once('load', cb)
loader.once('error', cb)
}
Voici un exemple JSFiddle - http://jsfiddle.net/6yL6t/
Et le code:
function hashCode(str) {
var hash = 0, i, chr, len;
if (str.length == 0) return hash;
for (i = 0, len = str.length; i < len; i++) {
chr = str.charCodeAt(i);
hash = ((hash << 5) - hash) + chr;
hash |= 0; // Convert to 32bit integer
}
return hash;
}
var onceHashes = {};
function once(func) {
var unique = hashCode(func.toString().match(/function[^{]+\{([\s\S]*)\}$/)[1]);
if (!onceHashes[unique]) {
onceHashes[unique] = true;
func();
}
}
Vous pourriez faire:
for (var i=0; i<10; i++) {
once(function() {
alert(i);
});
}
Et ça ne fonctionnera qu'une fois :)
La configuration initiale:
var once = function( once_fn ) {
var ret, is_called;
// return new function which is our control function
// to make sure once_fn is only called once:
return function(arg1, arg2, arg3) {
if ( is_called ) return ret;
is_called = true;
// return the result from once_fn and store to so we can return it multiply times:
// you might wanna look at Function.prototype.apply:
ret = once_fn(arg1, arg2, arg3);
return ret;
};
}
Fonction invalidate
réutilisable qui fonctionne avec setInterval
:
var myFunc = function (){
if (invalidate(arguments)) return;
console.log('called once and never again!'); // your stuff here
};
const invalidate = function(a) {
var fired = a.callee.fired;
a.callee.fired = true;
return fired;
}
setInterval(myFunc, 1000);
Essayez-le sur JSBin: https://jsbin.com/vicipar/edit?js,console
Variation de réponse de Bunyk
Essayer d'utiliser la fonction de soulignement "une fois":
var initialize = _.once(createApplication);
initialize();
initialize();
// Application is only created once.
Si vous utilisez Ramda, vous pouvez utiliser la fonction "une fois" .
Une citation de la documentation:
une fois Fonction (a… → b) → (a… → b) PARAMÈTRES ajoutés dans la v0.1.0
Accepte une fonction fn et renvoie une fonction qui protège l'appel de fn de telle sorte que fn ne puisse être appelé qu'une seule fois, quel que soit le nombre de fois où la fonction renvoyée est appelée. La première valeur calculée est renvoyée dans les invocations suivantes.
var addOneOnce = R.once(x => x + 1);
addOneOnce(10); //=> 11
addOneOnce(addOneOnce(50)); //=> 11
décorateur simple qui facile à écrire quand vous avez besoin
function one(func) {
return function () {
func && func.apply(this, arguments);
func = null;
}
}
en utilisant:
var initializer= one( _ =>{
console.log('initializing')
})
initializer() // 'initializing'
initializer() // nop
initializer() // nop
garder aussi simple que possible
function sree(){
console.log('hey');
window.sree = _=>{};
}
Vous pouvez voir le résultat
var init = function() {
console.log("logges only once");
init = false;
};
if(init) { init(); }
/* next time executing init() will cause error because now init is
-equal to false, thus typing init will return false; */