28 votes

JavaScript - exécuté une fois sans booléens

Y at - il un moyen d'exécuter un morceau de code JavaScript qu'UNE SEULE FOIS, sans utiliser des variables de drapeau booléennes de se rappeler si elle a déjà été RAN ou non?

Plus précisément pas quelque chose comme:

 var alreadyRan = false;
function runOnce() {
  if (alreadyRan) {
    return;
  }
  alreadyRan = true;

  /* do stuff here */

}
 

Je vais avoir beaucoup de ces types de fonctions et garder tous les booléens serait désordonné ...

71voto

Lekensteyn Points 22873

Une alternative qui remplace une fonction lors de l'exécution, de sorte qu'il sera exécuté qu'une seule fois.

function useThisFunctionOnce(){
   // overwrite this function, so it will be executed only once
   useThisFunctionOnce = Function("");
   // real code below
   alert("Hi!");
}

// displays "Hi!"
useThisFunctionOnce();
// does nothing
useThisFunctionOnce();

"Utile" exemple:

var preferences = {};
function read_preferences(){
   // read preferences once
   read_preferences = Function("");
   // load preferences from storage and save it in 'preferences'
}
function readPreference(pref_name){
    read_prefences();
    return preferences.hasOwnProperty(pref_name) ? preferences[pref_name] : '';
}
if(readPreference('like_javascript') != 'yes'){
   alert("What's wrong wth you?!");
}
alert(readPreference('is_stupid') ? "Stupid!" : ":)");

Edit: comme CMS souligné, tout en écrasant l'ancienne fonction avec function(){} permettra de créer une fermeture dans laquelle les anciens variables existent encore. Pour contourner ce problème, function(){} est remplacé par Function(""). Cela va créer une fonction vide dans la portée globale, en évitant les bouchons.

9voto

Mike Robinson Points 12273

J'aime l'implémentation de Lekensteyn, mais vous pouvez également avoir une seule variable pour stocker les fonctions exécutées. Le code ci-dessous doit exécuter "runOnce" et "runAgain" à la fois. C'est toujours booléen, mais il semble que vous ne vouliez tout simplement pas beaucoup de variables.

 var runFunctions = {};

function runOnce() {
  if(!hasRun(arguments.callee)) {
   /* do stuff here */
   console.log("once");
  }
}

function runAgain() {
  if(!hasRun(arguments.callee)) {
   /* do stuff here */
   console.log("again");
  }
}


function hasRun(functionName) {
 functionName = functionName.toString();
 functionName = functionName.substr('function '.length);
 functionName = functionName.substr(0, functionName.indexOf('('));

 if(runFunctions[functionName]) {
   return true;
 } else {
   runFunctions[functionName] = true;
   return false;
 }
}

runOnce();
runAgain();
runAgain();
 

8voto

Brian Nickel Points 8687

Un problème avec un certain nombre de ces approches est qu'elles dépendent des noms de fonction de travail: Mike approche ne fonctionnera pas si vous créez une fonction avec "x = function() ..." et Lekensteyn l'approche ne fonctionnera pas si vous définissez x = useThisFunctionOnce avant useThisFunctionOnce est appelé.

Je recommande l'utilisation de Russ fermeture de l'approche si vous voulez courir à droite à l'écart ou de l'approche par Underscore.js si vous voulez retarder l'exécution:

function once(func) {
    var ran = false, memo;
    return function() {
        if (ran) return memo;
        ran = true;
        return memo = func.apply(this, arguments);
    };
}

var myFunction = once(function() {
    return new Date().toString();
});

setInterval(function() {console.log(myFunction());}, 1000);

Sur la première exécution, la fonction interne est exécuté et les résultats sont retournés. Sur les pistes, le résultat original de l'objet est retourné.

6voto

Russ Cam Points 58168

Qu'entend immédiatement appelée fonction anonyme?

(function () {

    // code in here to run once

})();

le code s'exécute immédiatement et ne laisse pas de trace dans l'espace de noms global.

Si ce code devra être appelée à partir d'ailleurs, puis une clôture peut être utilisé pour s'assurer que le contenu d'une fonction sont exécutés qu'une seule fois. Personnellement, je préfère cela à une fonction qui réécrit lui-même car j'ai l'impression de le faire peut causer de la confusion, mais à chacun son propre :) Cette application tire parti du fait que 0 est une falsy valeur.

var once = (function() {
  var hasRun = 0;  
  return function () {
    if (!hasRun) {
      hasRun++;   

      // body to run only once

      // log to the console for a test       
      console.log("only ran once");
    }              
  }
})();

// test that the body of the function executes only once
for (var i = 0; i < 5; i++) 
  once();

3voto

dmi3y Points 1520

Solution élégante de Douglas Crockford, passé un certain temps à comprendre comment il fonctionne et je suis tombé sur ce fil.

Donc, l'emballage, une fois de retour de la fonction qui est juste invoque paramètre de la fonction que vous avez passé. Et, profitant de fermetures de cette construction remplacé passé de fonction de fonction vide, ou la valeur null dans la source d'origine, après le premier appel, de sorte que tous les prochains appels seront inutiles.

C'est quelque chose de très proche à toutes les autres réponses, mais c'est un peu de l'auto contenant du code et vous pouvez l'utiliser de façon indépendante, ce qui est bon. Je suis encore à essayer de saisir tout l'ensemble du dispositif de remplacement, mais en pratique, il fonctionne parfaitement.

function once (func) {

 return function () {
   var f = func;
   func = null;
   return f.apply(this, arguments);
 };

}

function hi(name) {
  console.log("Hi %s", name);
}

sayonce = once(hi);
sayonce("Vasya");
sayonce("Petya");

pour ceux qui sont curieux, voici jsbin transformations

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X