Ik heb een constructor functie die een event handler registreert:
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);
Echter, ik kan geen toegang krijgen tot de data
eigenschap van het gemaakte object in de callback. Het lijkt erop dat this
niet verwijst naar het object dat is aangemaakt, maar naar een ander object.
Ik heb ook geprobeerd om een object methode te gebruiken in plaats van een anonieme functie:
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', this.alert);
}
MyConstructor.prototype.alert = function() {
alert(this.name);
};
maar het vertoont dezelfde problemen.
Hoe krijg ik toegang tot het juiste object?
this
this
(aka "the context") is een speciaal sleutelwoord binnen elke functie en de waarde ervan hangt alleen af van hoe de functie is aangeroepen, niet hoe/wanneer/waar deze is gedefinieerd. Het wordt niet beïnvloed door lexicale scopes zoals andere variabelen (behalve voor pijl functies, zie hieronder). Hier zijn enkele voorbeelden:
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`
this
, kijk eens naar de [MDN documentatie][this].this
this
nietJe wilt eigenlijk geen toegang tot this
in het bijzonder, maar tot het object waarnaar het verwijst. Daarom'is een gemakkelijke oplossing om gewoon een nieuwe variabele te maken die ook naar dat object verwijst. De variabele kan elke naam hebben, maar veel voorkomende zijn self
en that
.
function MyConstructor(data, transport) {
this.data = data;
var self = this;
transport.on('data', function() {
alert(self.data);
});
}
Omdat self
een normale variabele is, gehoorzaamt het aan lexical scope regels en is toegankelijk binnen de callback. Dit heeft ook het voordeel dat je toegang hebt tot de this
waarde van de callback zelf.
this
van de callback instellen - deel 1Het lijkt misschien alsof je geen controle hebt over de waarde van this
omdat de waarde automatisch wordt ingesteld, maar dat is eigenlijk niet het geval.
Elke functie heeft de methode .bind
[docs], die een nieuwe functie teruggeeft met this
gebonden aan een waarde. De functie heeft precies hetzelfde gedrag als de functie die je .bind
hebt aangeroepen, alleen is this
door jou ingesteld. Het maakt niet uit hoe of wanneer die functie wordt aangeroepen, this
zal altijd verwijzen naar de doorgegeven waarde.
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);
}
In dit geval binden we de callback's this
aan de waarde van MijnConstructor
's this
.
Note: Bij het binden van context voor jQuery, gebruik in plaats daarvan jQuery.proxy
[docs]. De reden om dit te doen is zodat je de referentie naar de functie niet hoeft op te slaan wanneer je een event callback unbindt. jQuery handelt dat intern af.
ECMAScript 6 introduceert pijl functies, die kunnen worden gezien als lambda functies. Ze hebben geen eigen this
binding. In plaats daarvan wordt this
opgezocht in scope net als een normale variabele. Dat betekent dat je .bind
niet hoeft aan te roepen. Dat's niet het enige speciale gedrag dat ze hebben, raadpleeg de MDN documentatie voor meer informatie.
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', () => alert(this.data));
}
this
van de callback in - deel 2Sommige functies/methoden die callbacks accepteren accepteren ook een waarde waarnaar de this
van de callback's moet verwijzen. Dit is in principe hetzelfde als zelf binden, maar de functie/methode doet het voor je. Array#map
[docs] is zo'n methode. Zijn handtekening is:
array.map(callback[, thisArg])
Het eerste argument is de callback en het tweede argument is de waarde waar this
naar moet verwijzen. Hier is een gekunsteld voorbeeld:
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
Note: Of je al dan niet een waarde voor this
kunt doorgeven, wordt meestal vermeld in de documentatie van die functie/methode. Bijvoorbeeld, jQuery's $.ajax
methode [docs] beschrijft een optie genaamd context
:
Dit object wordt de context van alle Ajax-gerelateerde callbacks.
Veel voorkomend probleem: Object methodes gebruiken als callbacks/event handlers
Een andere veel voorkomende manifestatie van dit probleem is wanneer een object methode wordt gebruikt als callback/event handler. Functies zijn eersteklas burgers in JavaScript en de term "method" is gewoon een spreektaal voor een functie die een waarde is van een object eigenschap. Maar die functie'heeft geen specifieke link naar zijn "containing" object. Beschouw het volgende voorbeeld:
function Foo() {
this.data = 42,
document.body.onclick = this.method;
}
Foo.prototype.method = function() {
console.log(this.data);
};
De functie this.method
is toegewezen als click event handler, maar als op het document.body
wordt geklikt, zal de gelogde waarde undefined
zijn, omdat binnen de event handler, this
verwijst naar het document.body
, niet de instantie van Foo
.
Zoals in het begin al gezegd, waar this
naar verwijst hangt af van hoe de functie aangeroepen wordt, niet hoe deze gedefinieerd wordt.
Als de code als de volgende was, zou het misschien duidelijker zijn dat de functie geen impliciete verwijzing naar het object heeft'-:
function method() {
console.log(this.data);
}
function Foo() {
this.data = 42,
document.body.onclick = this.method;
}
Foo.prototype.method = method;
De oplossing is dezelfde als hierboven vermeld: Indien beschikbaar, gebruik .bind
om this
expliciet aan een specifieke waarde te binden
document.body.onclick = this.method.bind(this);
of roep de functie expliciet aan als een "method" van het object, door een anonieme functie te gebruiken als callback / event handler en wijs het object (this
) toe aan een andere variabele:
var self = this;
document.body.onclick = function() {
self.method();
};
of gebruik een pijl functie:
document.body.onclick = () => this.method();
Het zit allemaal in de "magic" syntaxis van het aanroepen van een methode:
object.property();
Als je de eigenschap uit het object haalt en het in één keer aanroept, zal het object de context voor de methode zijn. Als je dezelfde methode aanroept, maar in afzonderlijke stappen, is de context in plaats daarvan het globale bereik (venster):
var f = object.property;
f();
Wanneer je de referentie van een methode krijgt, is het niet langer aan het object gekoppeld, het is gewoon een referentie naar een gewone functie. Hetzelfde gebeurt wanneer je de referentie krijgt om te gebruiken als een callback:
this.saveNextLevelData(this.setAll);
Dat is waar je de context aan de functie bindt:
this.saveNextLevelData(this.setAll.bind(this));
Als je jQuery gebruikt, moet je in plaats daarvan de $.proxy
methode gebruiken, omdat bind
niet in alle browsers wordt ondersteund:
this.saveNextLevelData($.proxy(this.setAll, this));
De term "context" wordt soms gebruikt om te verwijzen naar het object waarnaar verwezen wordt door this. Het gebruik ervan is ongepast omdat het noch semantisch noch technisch past bij ECMAScript's this.
"Context" betekent de omstandigheden rond iets die betekenis toevoegen, of enige voorafgaande en volgende informatie die extra betekenis geeft. De term "context" wordt in ECMAScript gebruikt om te verwijzen naar uitvoeringscontext, dat zijn alle parameters, scope en dit binnen de scope van een of andere uitvoerende code.
Dit wordt getoond in ECMA-262 sectie 10.4.2:
Stel de ThisBinding in op dezelfde waarde als de ThisBinding van de aanroepende uitvoeringscontext
wat duidelijk aangeeft dat dit deel uitmaakt van een uitvoeringscontext.
Een uitvoeringscontext levert de omringende informatie die betekenis geeft aan code die wordt uitgevoerd. Het bevat veel meer informatie dan alleen de thisBinding.
Dus de waarde van this is'niet "context", het'is slechts één deel van een uitvoeringscontext. Het'is in wezen een lokale variabele die door de aanroep op elk object kan worden ingesteld en in strikte modus, op elke waarde überhaupt.