Comment accéder au "this" correct dans un rappel?

Nov 29 2013

J'ai une fonction constructeur qui enregistre un gestionnaire d'événements:

function MyConstructor(data, transport) {
    this.data = data;
    transport.on('data', function () {
        alert(this.data);
    });
}

// Mock transport object
var transport = {
    on: function(event, callback) {
        setTimeout(callback, 1000);
    }
};

// called as
var obj = new MyConstructor('foo', transport);

Cependant, je ne suis pas en mesure d'accéder à la datapropriété de l'objet créé dans le rappel. Il semble thisne pas faire référence à l'objet qui a été créé mais à un autre.

J'ai également essayé d'utiliser une méthode objet au lieu d'une fonction anonyme:

function MyConstructor(data, transport) {
    this.data = data;
    transport.on('data', this.alert);
}

MyConstructor.prototype.alert = function() {
    alert(this.name);
};

mais il présente les mêmes problèmes.

Comment puis-je accéder au bon objet?

Réponses

1910 FelixKling Nov 29 2013 at 13:13

Ce que vous devez savoir this

this(aka "le contexte") est un mot-clé spécial à l'intérieur de chaque fonction et sa valeur ne dépend que de la façon dont la fonction a été appelée, pas comment / quand / où elle a été définie. Il n'est pas affecté par les portées lexicales comme les autres variables (sauf pour les fonctions fléchées, voir ci-dessous). Voici quelques exemples:

function foo() {
    console.log(this);
}

// normal function call
foo(); // `this` will refer to `window`

// as object method
var obj = {bar: foo};
obj.bar(); // `this` will refer to `obj`

// as constructor function
new foo(); // `this` will refer to an object that inherits from `foo.prototype`

Pour en savoir plus this, consultez la documentation MDN .


Comment se référer à la bonne this

Utiliser les fonctions fléchées

ECMAScript 6 a introduit des fonctions fléchées , qui peuvent être considérées comme des fonctions lambda. Ils n'ont pas leur propre thisliaison. Au lieu de cela, thisest recherché dans la portée comme une variable normale. Cela signifie que vous n'avez pas besoin d'appeler .bind. Ce n'est pas le seul comportement spécial qu'ils ont, veuillez vous référer à la documentation MDN pour plus d'informations.

function MyConstructor(data, transport) {
    this.data = data;
    transport.on('data', () => alert(this.data));
}

Ne pas utiliser this

En fait, vous ne voulez pas accéder thisen particulier, mais à l'objet auquel il fait référence . C'est pourquoi une solution simple consiste simplement à créer une nouvelle variable qui fait également référence à cet objet. La variable peut avoir n'importe quel nom, mais les plus courants sont selfet that.

function MyConstructor(data, transport) {
    this.data = data;
    var self = this;
    transport.on('data', function() {
        alert(self.data);
    });
}

Puisqu'il selfs'agit d'une variable normale, elle obéit aux règles de portée lexicale et est accessible à l'intérieur du callback. Cela présente également l'avantage que vous pouvez accéder à la thisvaleur du rappel lui-même.

Définition explicite thisdu rappel - partie 1

Il peut sembler que vous n'ayez aucun contrôle sur la valeur de thiscar sa valeur est définie automatiquement, mais ce n'est en fait pas le cas.

Chaque fonction a la méthode .bind [docs] , qui renvoie une nouvelle fonction thisliée à une valeur. La fonction a exactement le même comportement que celui que vous avez appelé .bind, seulement celui que thisvous avez défini. Peu importe comment ou quand cette fonction est appelée, thisfera toujours référence à la valeur transmise.

function MyConstructor(data, transport) {
    this.data = data;
    var boundFunction = (function() { // parenthesis are not necessary
        alert(this.data);             // but might improve readability
    }).bind(this); // <- here we are calling `.bind()` 
    transport.on('data', boundFunction);
}

Dans ce cas, nous lions les rappels thisà la valeur de MyConstructor's this.

Remarque: Lors d'un contexte de liaison pour jQuery, utilisez plutôt jQuery.proxy [docs] . La raison pour cela est que vous n'ayez pas besoin de stocker la référence à la fonction lors de la dissociation d'un rappel d'événement. jQuery gère cela en interne.

Ensemble thisdu rappel - partie 2

Certaines fonctions / méthodes qui acceptent les rappels acceptent également une valeur à laquelle les rappels thisdoivent faire référence. C'est fondamentalement la même chose que le lier vous-même, mais la fonction / méthode le fait pour vous. Array#map [docs] est une telle méthode. Sa signature est:

array.map(callback[, thisArg])

Le premier argument est le rappel et le deuxième argument est la valeur à laquelle thisse référer. Voici un exemple artificiel:

var arr = [1, 2, 3];
var obj = {multiplier: 42};

var new_arr = arr.map(function(v) {
    return v * this.multiplier;
}, obj); // <- here we are passing `obj` as second argument

Remarque: si vous pouvez ou non transmettre une valeur pour thisest généralement mentionné dans la documentation de cette fonction / méthode. Par exemple, la $.ajaxméthode de jQuery [docs] décrit une option appelée context:

Cet objet deviendra le contexte de tous les rappels liés à Ajax.


Problème courant: utilisation de méthodes d'objet comme rappels / gestionnaires d'événements

Une autre manifestation courante de ce problème est lorsqu'une méthode objet est utilisée comme gestionnaire de rappel / événement. Les fonctions sont des citoyens de première classe en JavaScript et le terme «méthode» est juste un terme familier pour une fonction qui est une valeur d'une propriété d'objet. Mais cette fonction n'a pas de lien spécifique vers son objet "contenant".

Prenons l'exemple suivant:

function Foo() {
    this.data = 42,
    document.body.onclick = this.method;
}

Foo.prototype.method = function() {
    console.log(this.data);
};

La fonction this.methodest affectée en tant que gestionnaire d'événements de clic, mais si l'utilisateur document.bodyclique sur, la valeur consignée sera undefined, car à l'intérieur du gestionnaire d'événements, se thisréfère au document.body, pas à l'instance de Foo.
Comme déjà mentionné au début, ce thisà quoi se réfère dépend de la façon dont la fonction est appelée , et non de la façon dont elle est définie .
Si le code était comme le suivant, il pourrait être plus évident que la fonction n'a pas de référence implicite à l'objet:

function method() {
    console.log(this.data);
}


function Foo() {
    this.data = 42,
    document.body.onclick = this.method;
}

Foo.prototype.method = method;

La solution est la même que celle mentionnée ci-dessus: si disponible, utilisez .bindpour se lier explicitement thisà une valeur spécifique

document.body.onclick = this.method.bind(this);

ou appelez explicitement la fonction en tant que "méthode" de l'objet, en utilisant une fonction anonyme comme gestionnaire de rappel / événement et affectez l'objet ( this) à une autre variable:

var self = this;
document.body.onclick = function() {
    self.method();
};

ou utilisez une fonction fléchée:

document.body.onclick = () => this.method();
225 MohanDere Aug 13 2016 at 17:26

Voici plusieurs façons d'accéder au contexte parent dans le contexte enfant -

  1. Vous pouvez utiliser la bind()fonction.
  2. Stockez la référence au contexte / ceci dans une autre variable (voir l'exemple ci-dessous).
  3. Utilisez les fonctions ES6 Arrow .
  4. Modifier le code / la conception de la fonction / l'architecture - pour cela, vous devez maîtriser les modèles de conception en javascript.

1. Utiliser la bind()fonction

function MyConstructor(data, transport) {
    this.data = data;
    transport.on('data', ( function () {
        alert(this.data);
    }).bind(this) );
}
// Mock transport object
var transport = {
    on: function(event, callback) {
        setTimeout(callback, 1000);
    }
};
// called as
var obj = new MyConstructor('foo', transport);

Si vous utilisez underscore.js-http://underscorejs.org/#bind

transport.on('data', _.bind(function () {
    alert(this.data);
}, this));

2 Stockez la référence au contexte / ceci dans une autre variable

function MyConstructor(data, transport) {
  var self = this;
  this.data = data;
  transport.on('data', function() {
    alert(self.data);
  });
}

3 Flèche fonction

function MyConstructor(data, transport) {
  this.data = data;
  transport.on('data', () => {
    alert(this.data);
  });
}
59 Guffa May 21 2014 at 07:11

Tout est dans la syntaxe "magique" de l'appel d'une méthode:

object.property();

Lorsque vous récupérez la propriété de l'objet et que vous l'appelez en une seule fois, l'objet sera le contexte de la méthode. Si vous appelez la même méthode, mais en étapes séparées, le contexte est la portée globale (fenêtre) à la place:

var f = object.property;
f();

Lorsque vous obtenez la référence d'une méthode, elle n'est plus attachée à l'objet, c'est juste une référence à une fonction simple. La même chose se produit lorsque vous obtenez la référence à utiliser comme rappel:

this.saveNextLevelData(this.setAll);

C'est là que vous lieriez le contexte à la fonction:

this.saveNextLevelData(this.setAll.bind(this));

Si vous utilisez jQuery, vous devez utiliser la $.proxyméthode à la place, car elle bindn'est pas prise en charge dans tous les navigateurs:

this.saveNextLevelData($.proxy(this.setAll, this));
34 RobG Jun 01 2014 at 07:44

Le problème avec le "contexte"

Le terme «contexte» est parfois utilisé pour désigner l'objet référencé par celui-ci . Son utilisation est inappropriée parce qu'elle ne correspond pas non plus sémantiquement ou techniquement avec de ECMAScript ce .

«Contexte» signifie les circonstances entourant quelque chose qui ajoute du sens, ou certaines informations précédentes et suivantes qui donnent une signification supplémentaire. Le terme «contexte» est utilisé dans ECMAScript pour désigner le contexte d'exécution , qui est l'ensemble des paramètres, la portée, et ceci dans le cadre de certains codes d'exécution.

Ceci est illustré dans la section 10.4.2 de l'ECMA-262 :

Définissez ThisBinding sur la même valeur que ThisBinding du contexte d'exécution appelant

ce qui indique clairement que cela fait partie d'un contexte d'exécution.

Un contexte d'exécution fournit les informations environnantes qui ajoutent du sens au code en cours d'exécution. Il comprend beaucoup plus d'informations que simplement thisBinding .

Donc, la valeur de ce n'est pas "contexte", c'est juste une partie d'un contexte d'exécution. C'est essentiellement une variable locale qui peut être définie par l'appel à n'importe quel objet et en mode strict, à n'importe quelle valeur.

32 Ashish Jan 30 2019 at 18:01

Vous devez connaître le mot clé "this".

Selon mon avis, vous pouvez implémenter "ceci" de trois manières (fonction Self / Arrow / Bind Method)

Ce mot-clé d'une fonction se comporte un peu différemment en JavaScript par rapport aux autres langages.

Il présente également quelques différences entre le mode strict et le mode non strict.

Dans la plupart des cas, la valeur de ceci est déterminée par la façon dont une fonction est appelée.

Il ne peut pas être défini par affectation pendant l'exécution, et il peut être différent à chaque fois que la fonction est appelée.

ES5 a introduit la méthode bind () pour définir la valeur de this d'une fonction indépendamment de la façon dont elle est appelée,

et ES2015 a introduit des fonctions fléchées qui ne fournissent pas leur propre liaison (il conserve cette valeur du contexte lexical englobant).

Méthode 1: Self - Self est utilisé pour maintenir une référence à l'original même lorsque le contexte change. C'est une technique souvent utilisée dans les gestionnaires d'événements (en particulier dans les fermetures).

Référence :https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this

function MyConstructor(data, transport) {
    this.data = data;
    var self = this;
    transport.on('data', function () {
        alert(self.data);
    });
}

Méthode 2 : Fonction de flèche - Une expression de fonction de flèche est une alternative syntaxiquement compacte à une expression de fonction régulière,

bien que sans ses propres liaisons avec les mots-clés this, arguments, super ou new.target.

Les expressions de fonction de flèche ne conviennent pas comme méthodes et ne peuvent pas être utilisées comme constructeurs.

Référence :https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions

  function MyConstructor(data, transport) {
    this.data = data;
    transport.on('data',()=> {
        alert(this.data);
    });
}

Méthode 3 : Bind - La méthode bind () crée une nouvelle fonction qui,

lorsqu'il est appelé, ce mot-clé est défini sur la valeur fournie,

avec une séquence donnée d'arguments précédant tout fourni lorsque la nouvelle fonction est appelée.

Référence: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Function/bind

  function MyConstructor(data, transport) {
    this.data = data;
    transport.on('data',(function() {
        alert(this.data);
    }).bind(this);
26 AL-zami Aug 19 2017 at 00:58

Tout d'abord, vous devez avoir une compréhension claire scopeet un comportement du thismot - clé dans le contexte de scope.

this& scope:


there are two types of scope in javascript. They are :

   1) Global Scope

   2) Function Scope

En bref, la portée globale fait référence à l'objet window. Les variables déclarées dans une portée globale sont accessibles de n'importe où. D'autre part, la portée de la fonction réside à l'intérieur d'une fonction. La variable déclarée à l'intérieur d'une fonction n'est normalement pas accessible depuis le monde extérieur. thisLe mot clé dans la portée globale fait référence à l'objet window. thisLa fonction inside fait également référence à l'objet window. Ainsi, thisnous référons toujours à la fenêtre jusqu'à ce que nous trouvions un moyen de manipuler thispour indiquer un contexte de notre choix.

--------------------------------------------------------------------------------
-                                                                              -
-   Global Scope                                                               -
-   ( globally "this" refers to window object)                                 -     
-                                                                              -
-         function outer_function(callback){                                   -
-                                                                              -
-               // outer function scope                                        -
-               // inside outer function"this" keyword refers to window object -                                                                              -
-              callback() // "this" inside callback also refers window object  -

-         }                                                                    -
-                                                                              -
-         function callback_function(){                                        -
-                                                                              -
-                //  function to be passed as callback                         -
-                                                                              -
-                // here "THIS" refers to window object also                   -
-                                                                              -
-         }                                                                    -
-                                                                              -
-         outer_function(callback_function)                                    -
-         // invoke with callback                                              -
--------------------------------------------------------------------------------

Différentes façons de manipuler thisles fonctions de rappel à l'intérieur:

Ici, j'ai une fonction constructeur appelée Person. Il possède une propriété appelée nameet quatre méthode appelée sayNameVersion1, sayNameVersion2, sayNameVersion3, sayNameVersion4. Tous les quatre ont une tâche spécifique. Acceptez un rappel et appelez-le. Le rappel a une tâche spécifique qui consiste à consigner la propriété name d'une instance de la fonction de constructeur Person.

function Person(name){

    this.name = name

    this.sayNameVersion1 = function(callback){
        callback.bind(this)()
    }
    this.sayNameVersion2 = function(callback){
        callback()
    }

    this.sayNameVersion3 = function(callback){
        callback.call(this)
    }

    this.sayNameVersion4 = function(callback){
        callback.apply(this)
    }

}

function niceCallback(){

    // function to be used as callback

    var parentObject = this

    console.log(parentObject)

}

Créons maintenant une instance à partir du constructeur de personne et invoquons différentes versions de la sayNameVersionXméthode (X se réfère à 1, 2, 3, 4) avec niceCallbackpour voir de combien de façons nous pouvons manipuler le thisrappel interne pour faire référence à l' personinstance.

var p1 = new Person('zami') // create an instance of Person constructor

lier :

Ce que bind fait est de créer une nouvelle fonction avec le thismot - clé défini sur la valeur fournie.

sayNameVersion1et sayNameVersion2utilisez bind pour manipuler thisla fonction de rappel.

this.sayNameVersion1 = function(callback){
    callback.bind(this)()
}
this.sayNameVersion2 = function(callback){
    callback()
}

le premier se lie thisavec un rappel à l'intérieur de la méthode elle-même, et pour le second, un rappel est passé avec l'objet qui lui est lié.

p1.sayNameVersion1(niceCallback) // pass simply the callback and bind happens inside the sayNameVersion1 method

p1.sayNameVersion2(niceCallback.bind(p1)) // uses bind before passing callback

appeler :

Le first argumentde la callméthode est utilisé comme thisà l'intérieur de la fonction qui est invoquée avec qui lui est callattachée.

sayNameVersion3utilise callpour manipuler le thispour faire référence à l'objet personne que nous avons créé, au lieu de l'objet fenêtre.

this.sayNameVersion3 = function(callback){
    callback.call(this)
}

et il est appelé comme suit:

p1.sayNameVersion3(niceCallback)

appliquer :

Similaire à call, le premier argument de applyfait référence à l'objet qui sera indiqué par thismot-clé.

sayNameVersion4utilise applypour manipuler thispour faire référence à un objet personne

this.sayNameVersion4 = function(callback){
    callback.apply(this)
}

et il est appelé comme suit. Simplement le rappel est passé,

p1.sayNameVersion4(niceCallback)
20 DattaChanewad Nov 17 2017 at 21:32

Nous ne pouvons pas lier cela à setTimeout(), car il s'exécute toujours avec un objet global (Window) , si vous voulez accéder au thiscontexte dans la fonction de rappel, puis en utilisant bind()la fonction de rappel, nous pouvons réaliser comme:

setTimeout(function(){
    this.methodName();
}.bind(this), 2000);
13 Code_Mode Feb 18 2019 at 16:10

La question tourne autour du thiscomportement des mots clés en javascript. thisse comporte différemment comme ci-dessous,

  1. La valeur de thisest généralement déterminée par un contexte d'exécution de fonction.
  2. Dans la portée globale, thisfait référence à l'objet global (l' windowobjet).
  3. Si le mode strict est activé pour une fonction, la valeur de thissera undefinedcomme en mode strict, objet global se réfère à la undefinedplace de l' windowobjet.
  4. L'objet qui se trouve avant le point est ce à quoi ce mot-clé sera lié.
  5. Nous pouvons définir la valeur de cette explicitement call(), bind()etapply()
  6. Lorsque le newmot-clé est utilisé (un constructeur), celui-ci est lié au nouvel objet en cours de création.
  7. Les fonctions fléchées ne sont pas liées this - à la place, thissont liées lexicalement (c'est-à-dire basées sur le contexte d'origine)

Comme la plupart des réponses le suggèrent, nous pouvons utiliser la fonction Flèche ou bind()Méthode ou Auto var. Je citerais un point sur les lambdas (fonction Flèche) du guide de style JavaScript de Google

Préférez utiliser les fonctions fléchées sur f.bind (this), et en particulier sur goog.bind (f, this). Évitez d'écrire const self = this. Les fonctions fléchées sont particulièrement utiles pour les rappels, qui transmettent parfois des arguments supplémentaires de manière inattendue.

Google recommande clairement d'utiliser des lambdas plutôt que de lier ou const self = this

La meilleure solution serait donc d'utiliser des lambdas comme ci-dessous,

function MyConstructor(data, transport) {
  this.data = data;
  transport.on('data', () => {
    alert(this.data);
  });
}

Les références:

  1. https://medium.com/tech-tajawal/javascript-this-4-rules-7354abdb274c
  2. flèches-fonctions-vs-lier
8 skyboyer Sep 22 2018 at 20:38

Actuellement, il existe une autre approche possible si les classes sont utilisées dans le code.

Avec la prise en charge des champs de classe, il est possible de procéder de la manière suivante:

class someView {
    onSomeInputKeyUp = (event) => {
        console.log(this); // this refers to correct value
    // ....
    someInitMethod() {
        //...
        someInput.addEventListener('input', this.onSomeInputKeyUp)

Bien sûr, sous le capot, toutes les bonnes fonctions fléchées relient le contexte, mais sous cette forme, cela semble beaucoup plus clair que la liaison explicite.

Comme il s'agit de la proposition de l'étape 3, vous aurez besoin de babel et du plugin babel approprié pour le traiter comme pour le moment (08/2018).

5 AndreaPuddu Aug 28 2018 at 16:10

Une autre approche, qui est la manière standard depuis DOM2 de se lier thisdans l'écouteur d'événements, qui vous permet de toujours supprimer l'écouteur (entre autres avantages), est la handleEvent(evt)méthode de l' EventListenerinterface:

var obj = {
  handleEvent(e) {
    // always true
    console.log(this === obj);
  }
};

document.body.addEventListener('click', obj);

Des informations détaillées sur l'utilisation handleEventpeuvent être trouvées ici:https://medium.com/@WebReflection/dom-handleevent-a-cross-platform-standard-since-year-2000-5bf17287fd38

2 WillemvanderVeen May 05 2020 at 15:48

this dans JS:

La valeur de thisdans JS est déterminée à 100% par la manière dont une fonction est appelée et non par la manière dont elle est définie. Nous pouvons trouver relativement facilement la valeur de thispar la `` règle à gauche du point '' :

  1. Lorsque la fonction est créée à l'aide du mot-clé function, la valeur de thisest l'objet à gauche du point de la fonction qui est appelée
  2. S'il n'y a pas d'objet à gauche du point, la valeur de l' thisintérieur d'une fonction est souvent l'objet global ( globaldans le nœud, windowdans le navigateur). Je ne recommanderais pas d'utiliser le thismot - clé ici car il est moins explicite que d'utiliser quelque chose comme window!
  3. Il existe certaines constructions comme les fonctions fléchées et les fonctions créées à l'aide de la Function.prototype.bind()fonction a qui peuvent fixer la valeur de this. Ce sont des exceptions à la règle, mais elles sont vraiment utiles pour fixer la valeur de this.

Exemple dans nodeJS

module.exports.data = 'module data';
// This outside a function in node refers to module.exports object
console.log(this);

const obj1 = {
    data: "obj1 data",
    met1: function () {
        console.log(this.data);
    },
    met2: () => {
        console.log(this.data);
    },
};

const obj2 = {
    data: "obj2 data",
    test1: function () {
        console.log(this.data);
    },
    test2: function () {
        console.log(this.data);
    }.bind(obj1),
    test3: obj1.met1,
    test4: obj1.met2,
};

obj2.test1();
obj2.test2();
obj2.test3();
obj2.test4();
obj1.met1.call(obj2);

Production:

Laissez-moi vous guider à travers les sorties 1 par 1 (en ignorant le premier journal à partir du second):

  1. thisest à obj2cause de la gauche de la règle de point, nous pouvons voir comment test1est appelé obj2.test1();. obj2est à gauche du point et donc de la thisvaleur.
  2. Même s'il obj2reste du point, il test2est lié obj1via la bind()méthode. Donc, la thisvaleur est obj1.
  3. obj2est à gauche du point de la fonction qui est appelée: obj2.test3(). Par conséquent, obj2sera la valeur de this.
  4. Dans ce cas: obj2.test4() obj2est à gauche du point. Cependant, les fonctions fléchées n'ont pas leur propre thisliaison. Par conséquent, il sera lié à la thisvaleur de la portée externe qui est module.exportsun objet qui a été enregistré au début.
  5. Nous pouvons également spécifier la valeur de thisen utilisant la callfonction. Ici, nous pouvons passer la thisvaleur souhaitée comme argument, ce qui est obj2dans ce cas.
1 Md.TazbirUrRahmanBhuiyan Oct 12 2020 at 23:07

Je problème auquel est confronté avec Ngxgraphique en ligne xAxisTickFormattingfonction qui a été appelée à partir de HTML comme ceci: [xAxisTickFormatting]="xFormat". Je n'ai pas pu accéder à la variable de mon composant à partir de la fonction déclarée. Cette solution m'a aidé à résoudre le problème pour trouver le correct. J'espère que cela aidera le Ngxgraphique linéaire, les utilisateurs.

au lieu d'utiliser la fonction comme celle-ci:

xFormat (value): string {
  return value.toString() + this.oneComponentVariable; //gives wrong result 
}

Utilisez ceci:

 xFormat = (value) => {
   // console.log(this);
   // now you have access to your component variables
   return value + this.oneComponentVariable
 }