web-dev-qa-db-fra.com

Passer le contexte "this" correct au rappel setTimeout?

Comment passer du contexte dans setTimeout? Je souhaite appeler this.tip.destroy() si this.options.destroyOnHide après 1000 ms. Comment puis je faire ça?

if (this.options.destroyOnHide) {
     setTimeout(function() { this.tip.destroy() }, 1000);
} 

Lorsque j'essaie ce qui précède, this fait référence à la fenêtre.

221
JamesBrownIsDead

EDIT: En résumé, lorsque l’on a posé cette question à 2010, le moyen le plus courant de résoudre ce problème était de sauvegarder une référence au contexte dans lequel setTimeout L'appel à la fonction est effectué, car setTimeout exécute la fonction avec this pointant vers l'objet global:

var that = this;
if (this.options.destroyOnHide) {
     setTimeout(function(){ that.tip.destroy() }, 1000);
} 

Dans la spécification ES5, publiée juste un an avant cette date, elle introduisait la méthode bind , cela n'était pas suggéré dans la réponse d'origine car elle n'était pas encore largement prise en charge et qu'il vous fallait plusieurs remplissages pour l'utiliser. mais maintenant c'est partout:

if (this.options.destroyOnHide) {
     setTimeout(function(){ this.tip.destroy() }.bind(this), 1000);
}

La fonction bind crée une nouvelle fonction avec la valeur this préremplie.

Maintenant, dans JS moderne, c’est exactement le problème que les fonctions de flèche résolvent en ES6 :

if (this.options.destroyOnHide) {
     setTimeout(() => { this.tip.destroy() }, 1000);
}

Les fonctions fléchées ne possèdent pas de valeur this. Lorsque vous y accédez, vous accédez à la valeur this de la portée lexicale englobante.

HTML5 aussi minuteries normalisées en 2011, et vous pouvez maintenant passer des arguments à la fonction de rappel:

if (this.options.destroyOnHide) {
     setTimeout(function(that){ that.tip.destroy() }, 1000, this);
}

Voir également:

293
CMS

Il existe des raccourcis prêts à l'emploi (sucre syntaxique) pour la fonction wrapper @CMS avec laquelle on répond. (Ci-dessous, en supposant que le contexte souhaité est _this.tip_.)


ECMAScript 5 ( navigateurs actuels , Node.js) et Prototype.js

Si vous ciblez navigateur compatible avec ECMA-262, 5ème édition (ECMAScript 5) ou Node.js , vous pouvez utiliser Function.prototype.bind . Vous pouvez éventuellement passer n'importe quel argument de fonction pour créer fonctions partielles .

_fun.bind(thisArg[, arg1[, arg2[, ...]]])
_

Encore une fois, dans votre cas, essayez ceci:

_if (this.options.destroyOnHide) {
    setTimeout(this.tip.destroy.bind(this.tip), 1000);
}
_

La même fonctionnalité a également été implémentée dans Prototype (toute autre bibliothèque?).

_Function.prototype.bind_ peut être implémenté comme ceci si vous voulez une compatibilité ascendante personnalisée (mais veuillez respecter les notes).


ECMAScript 2015 ( certains navigateurs , Node.js 5.0.0+)

Pour un développement à la pointe de la technologie (2015), vous pouvez utiliser les grandes fonctions de flèche , qui sont élément de la spécification ECMAScript 2015 (Harmony/ES6/ES2015) ( exemples ).

Une expression de fonction de flèche (également appelée fonction de flèche épaisse ) a une syntaxe plus courte que celle des expressions de fonction et lie lexicalement la valeur this [...].

_(param1, param2, ...rest) => { statements }
_

Dans votre cas, essayez ceci:

_if (this.options.destroyOnHide) {
    setTimeout(() => { this.tip.destroy(); }, 1000);
}
_

jQuery

Si vous utilisez déjà jQuery 1.4+, il existe une fonction toute prête pour définir explicitement le contexte this d'une fonction.

jQuery.proxy () : Prend une fonction et en retourne une nouvelle qui aura toujours un contexte particulier.

_$.proxy(function, context[, additionalArguments])
_

Dans votre cas, essayez ceci:

_if (this.options.destroyOnHide) {
    setTimeout($.proxy(this.tip.destroy, this.tip), 1000);
}
_

nderscore.js , lodash

Il est disponible dans Underscore.js, ainsi que lodash, ainsi que _.bind(...)1 , 2

bind Lier une fonction à un objet, ce qui signifie que chaque fois que la fonction est appelée, la valeur de this sera être l'objet. Vous pouvez également associer des arguments à la fonction pour les pré-remplir, également appelée application partielle.

__.bind(function, object, [*arguments])
_

Dans votre cas, essayez ceci:

_if (this.options.destroyOnHide) {
    setTimeout(_.bind(this.tip.destroy, this.tip), 1000);
}
_

bindjquerynderscore.jsecmascript-5prototypejsnoeud. js

221
Joel Purra

Dans les navigateurs autres qu'Internet Explorer, vous pouvez transmettre les paramètres à la fonction ensemble après le délai:

var timeoutID = window.setTimeout(func, delay, [param1, param2, ...]);

Donc, vous pouvez faire ceci:

var timeoutID = window.setTimeout(function (self) {
  console.log(self); 
}, 500, this);

C’est mieux en termes de performances qu’une recherche d’étendue (mise en cache de this dans une variable en dehors de l’expression de délai d’intervalle/intervalle), puis création d’une fermeture (en utilisant $.proxy ou Function.prototype.bind) .

Le code pour le faire fonctionner dans les IE de Webreflection :

/*@cc_on
(function (modifierFn) {
  // you have to invoke it as `window`'s property so, `window.setTimeout`
  window.setTimeout = modifierFn(window.setTimeout);
  window.setInterval = modifierFn(window.setInterval);
})(function (originalTimerFn) {
    return function (callback, timeout){
      var args = [].slice.call(arguments, 2);
      return originalTimerFn(function () { 
        callback.apply(this, args) 
      }, timeout);
    }
});
@*/
30
Misha Reyzlin

NOTE: Cela ne fonctionnera pas dans IE

var ob = {
    p: "ob.p"
}

var p = "window.p";

setTimeout(function(){
    console.log(this.p); // will print "window.p"
},1000); 

setTimeout(function(){
    console.log(this.p); // will print "ob.p"
}.bind(ob),1000);
3
gumkins

Si vous utilisez underscore , vous pouvez utiliser bind.

Par exemple.

if (this.options.destroyOnHide) {
     setTimeout(_.bind(this.tip.destroy, this), 1000);
}
2
Sam