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 peux pas accéder à la propriété data
de l'objet créé à l'intérieur du rappel. Il semble que this
ne se réfère pas à l'objet qui a été créé, mais à un autre.
J'ai aussi essayé d'utiliser une méthode d'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?
this
this
(ou "le contexte") est un mot clé spécial à l'intérieur de chaque fonction et sa valeur dépend uniquement de comment la fonction a été appelée, pas comment/quand/où a été défini. Il n'est pas affecté par les portées lexicales comme les autres variables (sauf pour les fonctions de flèche, 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 sur this
, jetez un œil à documentation MDN .
this
this
En fait, vous ne voulez pas accéder à this
en particulier, mais à l'objet auquel il fait référence . C'est pourquoi une solution simple consiste simplement à créer une nouvelle variable qui se réfère également à cet objet. La variable peut avoir n'importe quel nom, mais les plus communs sont self
et that
.
function MyConstructor(data, transport) {
this.data = data;
var self = this;
transport.on('data', function() {
alert(self.data);
});
}
Comme self
est une variable normale, elle obéit aux règles de portée lexicale et est accessible à l'intérieur du rappel. Cela présente également l’avantage de pouvoir accéder à la valeur this
du rappel lui-même.
this
du rappel - partie 1Il semblerait que vous n’ayez aucun contrôle sur la valeur de this
car sa valeur est définie automatiquement, mais ce n’est en réalité pas le cas.
Chaque fonction a la méthode .bind
[docs] , qui renvoie une nouvelle fonction avec this
liée à une valeur. La fonction a exactement le même comportement que celle que vous avez appelée .bind
on, seulement que this
a été défini par vous. Peu importe comment et quand cette fonction est appelée, this
fera 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 le this
du rappel à la valeur de MyConstructor
'this
.
Remarque: Lorsque vous liez le contexte pour jQuery, utilisez jQuery.proxy
[docs] à la place. La raison en est que vous n'avez pas besoin de stocker la référence à la fonction lorsque vous annulez la liaison d'un rappel d'événement. jQuery gère cela en interne.
ECMAScript 6 introduit les fonctions de flèche , que l’on peut considérer comme des fonctions lambda. Ils n'ont pas leur propre lien this
. Au lieu de cela, this
est recherché dans la portée comme une variable normale. Cela signifie que vous n'avez pas à appeler .bind
. Ce n'est pas le seul comportement spécial qu'ils ont, référez-vous à la documentation MDN pour plus d'informations.
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', () => alert(this.data));
}
this
du rappel - partie 2Certaines fonctions/méthodes qui acceptent les rappels acceptent également une valeur à laquelle le this
du rappel devrait faire référence. C'est fondamentalement la même chose que de 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 second argument est la valeur à laquelle this
devrait faire référence. 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: Le fait que vous puissiez ou non transmettre une valeur pour this
est généralement mentionné dans la documentation de cette fonction/méthode. Par exemple, méthode $.ajax
de jQuery [docs] décrit une option appelée context
:
Cet objet deviendra le contexte de tous les rappels liés à Ajax.
Une autre manifestation courante de ce problème est lorsqu'une méthode d'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.method
est affectée en tant que gestionnaire d’événements click, mais si vous cliquez sur document.body
, la valeur enregistrée sera undefined
, car dans le gestionnaire d’événements, this
fait référence à document.body
, pas l'instance de Foo
.
Comme déjà mentionné au début, ce à quoi this
dépend de la façon dont la fonction est appelée et non de la manière dont elle est défini .
Si le code ressemblait à ceci, il serait peut-être plus évident que la fonction n’ait 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 identique à celle mentionnée ci-dessus: Si disponible, utilisez .bind
pour lier explicitement this
à une valeur spécifique.
document.body.onclick = this.method.bind(this);
ou explicitement appeler la fonction en tant que "méthode" de l'objet, en utilisant une fonction anonyme en tant que callback/gestionnaire d'événements et attribuer l'objet (this
) à une autre variable:
var self = this;
document.body.onclick = function() {
self.method();
};
ou utilisez une fonction de flèche:
document.body.onclick = () => this.method();
bind()
. bind()
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));
function MyConstructor(data, transport) {
var self = this;
this.data = data;
transport.on('data', function() {
alert(self.data);
});
}
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', () => {
alert(this.data);
});
}
Tout est dans la syntaxe "magique" d'appeler une méthode:
object.property();
Lorsque vous obtenez la propriété de l'objet et que vous l'appelez d'un coup, l'objet sera le contexte de la méthode. Si vous appelez la même méthode, mais à des étapes distinctes, le contexte correspond à la portée globale (fenêtre):
var f = object.property;
f();
Lorsque vous obtenez la référence d'une méthode, celle-ci n'est plus attachée à l'objet, il s'agit simplement d'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 devriez plutôt utiliser la méthode $.proxy
, car bind
n'est pas pris en charge par tous les navigateurs:
this.saveNextLevelData($.proxy(this.setAll, this));
Le terme "contexte" est parfois utilisé pour désigner l'objet référencé par this. Son utilisation est inappropriée car elle ne correspond ni sémantiquement ni techniquement avec ECMAScript's this .
"Contexte" désigne les circonstances entourant quelque chose qui ajoute un sens, ou des informations précédentes et suivantes qui donnent un sens supplémentaire. Le terme "contexte" est utilisé dans ECMAScript pour faire référence à execution context , qui comprend tous les paramètres, la portée et this dans la portée de certains codes d'exécution.
Ceci est montré dans ECMA-262 section 10.4.2 :
Définissez ThisBinding sur la même valeur que ThisBinding de appel du contexte d'exécution
ce qui indique clairement que this fait partie d'un contexte d'exécution.
Un contexte d'exécution fournit les informations environnantes qui ajoutent une signification au code en cours d'exécution. Il inclut beaucoup plus d'informations que juste le thisBinding .
Donc, la valeur de this n'est pas "context", ce n'est qu'une partie d'un contexte d'exécution. Il s’agit essentiellement d’une variable locale qui peut être définie par l’appel de n’importe quel objet et en mode strict, pour n’importe quelle valeur.
Tout d'abord, vous devez avoir une compréhension claire de scope
et du comportement du mot clé this
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.variable déclarée à l'intérieur d'une fonction ne peut pas être accédée de l'extérieur du monde normalement .this
Le mot clé dans la portée globale fait référence à l'objet fenêtre .this
La fonction intérieure fait également référence à l'objet fenêtre. Ainsi, this
fera toujours référence à la fenêtre jusqu'à ce que nous trouvions un moyen de manipuler this
. pour 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 this
à l'intérieur des fonctions de rappel:
Ici, j'ai une fonction constructeur appelée Personne. Il a une propriété appelée name
et quatre méthodes appelées sayNameVersion1
, sayNameVersion2
, sayNameVersion3
, sayNameVersion4
. Tous les quatre ont une tâche spécifique. Accepter un rappel et l'invoquer. Le rappel a une tâche spécifique qui consiste à consigner la propriété name d'une instance de la fonction du 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 la personne et appelons différentes versions de la méthode sayNameVersionX
(X fait référence à 1,2,3,4) avec niceCallback
pour voir de nombreuses façons dont nous pouvons manipuler le this
dans callback pour faire référence à l'instance person
.
var p1 = new Person('zami') // create an instance of Person constructor
Qu'est-ce que bind fait est de créer une nouvelle fonction avec le mot clé this
défini sur la valeur fournie.
sayNameVersion1
et sayNameVersion2
utilisent bind pour manipuler this
de la fonction de rappel.
this.sayNameVersion1 = function(callback){
callback.bind(this)()
}
this.sayNameVersion2 = function(callback){
callback()
}
la première se lie this
avec un rappel à l'intérieur de la méthode elle-même.Et pour le deuxième 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
Le first argument
de la méthode call
est utilisé en tant que this
dans la fonction appelée avec call
qui lui est associé.
sayNameVersion3
utilise call
pour manipuler this
pour faire référence à l'objet personne que nous avons créé, au lieu de l'objet window.
this.sayNameVersion3 = function(callback){
callback.call(this)
}
et on l’appelle comme suit:
p1.sayNameVersion3(niceCallback)
Semblable à call
, le premier argument de apply
fait référence à l'objet qui sera indiqué par le mot clé this
.
sayNameVersion4
utilise apply
pour manipuler this
pour faire référence à un objet personne
this.sayNameVersion4 = function(callback){
callback.apply(this)
}
et il s’appelle comme suit. Il suffit de rappeler le rappel,
p1.sayNameVersion4(niceCallback)
Nous ne pouvons pas lier ceci à setTimeout()
, car il est toujours exécuté avec objet global (Window) , si vous souhaitez accéder au contexte this
dans la fonction de rappel, utilisez bind()
pour la fonction de rappel que vous pouvez obtenir comme suit:
setTimeout(function(){
this.methodName();
}.bind(this), 2000);
Vous devriez connaître le mot-clé "this".
Selon mon point de vue, vous pouvez implémenter "ceci" de trois manières(Self/Fonction Flèche/Méthode Bind)
Une fonction de ce mot clé se comporte un peu différemment en JavaScript par rapport aux autres langues.
Il existe également des différences entre le mode strict et le mode non strict.
Dans la plupart des cas, la valeur de cela dépend de la manière dont une fonction est appelée.
Il ne peut pas être défini par affectation lors de l'exécution et il peut être différent à chaque appel de la fonction.
ES5 a introduit la méthode bind () pour définir la valeur d'une fonction, quelle que soit sa dénomination.
et ES2015 a introduit des fonctions de flèche qui ne fournissent pas leur propre lien (il conserve la valeur this du contexte lexical englobant).
Method1: Self - Self est utilisé pour conserver une référence à l'original même si 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);
});
}
Method2: 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 à this, arguments, super, ou new.target, mots-clés.
Les expressions de fonction de flèche sont mal adaptées en tant que méthodes et ne peuvent pas être utilisées en tant que 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);
});
}
Method3: Bind- La méthode bind () crée une nouvelle fonction qui,
lorsqu'il est appelé, son mot-clé this est défini sur la valeur fournie,
avec une séquence d'arguments donnée précédant n'importe laquelle fournie 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);
Une autre approche, qui est méthode standard depuis DOM2 pour lier this
au sein de l'écouteur d'événements, qui vous permet de toujours supprimer l'écouteur (entre autres avantages), est la méthode handleEvent(evt)
de l'interface EventListener
. :
var obj = {
handleEvent(e) {
// always true
console.log(this === obj);
}
};
document.body.addEventListener('click', obj);
Des informations détaillées sur l’utilisation de handleEvent
sont disponibles ici: https://medium.com/@WebReflection/dom-handleevent-a-cross-platform-standard-since-year-2000-5bf17287fd38
Actuellement, une autre approche est possible si les classes sont utilisées dans le code.
Avec le support de class fields , il est possible de le faire 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, ce sont toutes de vieilles bonnes fonctions de flèche qui lient le contexte, mais sous cette forme, cela semble beaucoup plus clair que la liaison explicite.
Comme il s'agit de la proposition de stade 3, vous aurez besoin de babel et du plugin babel approprié pour le traiter comme pour le moment (08/2018).
La question porte sur le comportement du mot clé this
en javascript. this
se comporte différemment comme ci-dessous,
Comme le suggère la plupart des réponses, nous pouvons utiliser la fonction Arrow ou bind()
Method ou Self var. Je voudrais citer un point sur lambdas (fonction Arrow) de Google JavaScript Style Guide
Préfère utiliser les fonctions fléchées sur f.bind (this), et surtout sur goog.bind (f, this). Évitez d'écrire const auto = this. Fonctions fléchées sont particulièrement utiles pour les rappels, qui passent parfois inattendus arguments supplémentaires.
Google recommande clairement d'utiliser lambdas plutôt que bind ou const self = this
Donc, la meilleure solution serait d’utiliser des lambdas comme ci-dessous,
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', () => {
alert(this.data);
});
}
Références: