web-dev-qa-db-fra.com

Fonction en javascript pouvant être appelée une seule fois

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?

98
vlio20

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.

191
Ted Hopp

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
}
58
I Hate Lazy

Pointez sur une fonction vide une fois qu'elle a été appelée:

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>

Ou, comme si:

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)
25
vsync

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;
    };
  };
25
asawyer

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;
10
Bunyk
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
}
3
Shmiddty

essaye ça

var fun = (function() {
  var called = false;
  return function() {
    if (!called) {
      console.log("I  called");
      called = true;
    }
  }
})()
2
Database_Query

D'un mec nommé Crockford ... :)

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

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!
1
CMP

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)
}
1
orcaman

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 :)

1
Aldekein

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;
    };
}
1
andlrc

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

1
user300375

Essayer d'utiliser la fonction de soulignement "une fois":

var initialize = _.once(createApplication);
initialize();
initialize();
// Application is only created once.

http://underscorejs.org/#once

0
Andrew Feng

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
0
David

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
0
pery mimon

garder aussi simple que possible

function sree(){
  console.log('hey');
  window.sree = _=>{};
}

Vous pouvez voir le résultat

script result

0
Shreeketh K
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; */
0
RegarBoy