Cosa dovresti sapere this
this(aka "il contesto") è una parola chiave speciale all'interno di ciascuna funzione e il suo valore dipende solo da come è stata chiamata la funzione, non da come / quando / dove è stata definita. Non è influenzato da ambiti lessicali come altre variabili (ad eccezione delle funzioni freccia, vedere di seguito). Ecco alcuni esempi:
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`
Per saperne di più this, dai un'occhiata alla documentazione MDN .
Come fare riferimento al corretto this
Non usare this
In realtà non si desidera accedere thisin particolare, ma l'oggetto a cui si riferisce . Ecco perché una soluzione semplice è semplicemente creare una nuova variabile che si riferisce anche a quell'oggetto. La variabile può avere qualsiasi nome, ma quelli comuni sono selfe that.
function MyConstructor(data, transport) {
this.data = data;
var self = this;
transport.on('data', function() {
alert(self.data);
});
}
Poiché selfè una variabile normale, obbedisce alle regole dell'ambito lessicale ed è accessibile all'interno del callback. Ciò ha anche il vantaggio di poter accedere al thisvalore del callback stesso.
Set esplicito thisdel callback - parte 1
Potrebbe sembrare che tu non abbia alcun controllo sul valore di thisperché il suo valore è impostato automaticamente, ma in realtà non è così.
Ogni funzione ha il metodo .bind [docs] , che restituisce una nuova funzione thisassociata a un valore. La funzione ha esattamente lo stesso comportamento di quella che hai chiamato .bind, solo che è thisstata impostata da te. Indipendentemente da come o quando viene chiamata quella funzione, thisfarà sempre riferimento al valore passato.
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 questo caso, associamo i callback thisal valore di MyConstructor's this.
Nota: quando si associa un contesto per jQuery, utilizzare invece jQuery.proxy [docs] . La ragione per fare ciò è che non è necessario memorizzare il riferimento alla funzione quando si annulla un callback di un evento. jQuery lo gestisce internamente.
ECMAScript 6 introduce funzioni freccia , che possono essere pensate come funzioni lambda. Non hanno il loro thislegame. Invece, thisè cercato nell'ambito proprio come una normale variabile. Ciò significa che non devi chiamare .bind. Non è l'unico comportamento speciale che hanno, fare riferimento alla documentazione MDN per ulteriori informazioni.
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', () => alert(this.data));
}
Set thisdi callback - parte 2
Alcune funzioni / metodi che accettano i callback accettano anche un valore a cui thisdovrebbero fare riferimento i callback . Questo è fondamentalmente lo stesso che associarlo tu stesso, ma la funzione / metodo lo fa per te. Array#map [docs] è un tale metodo. La sua firma è:
array.map(callback[, thisArg])
Il primo argomento è il callback e il secondo argomento è il valore a cui thisdovrebbe fare riferimento. Ecco un esempio inventato:
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
Nota: la possibilità o meno di trasmettere un valore thisè di solito menzionata nella documentazione di tale funzione / metodo. Ad esempio, il $.ajaxmetodo [docs] di jQuery descrive un'opzione chiamata context:
Questo oggetto verrà inserito nel contesto di tutti i callback relativi ad Ajax.
Problema comune: utilizzo dei metodi oggetto come callback / gestori di eventi
Un'altra manifestazione comune di questo problema è quando un metodo oggetto viene utilizzato come gestore di callback / evento. Le funzioni sono cittadini di prima classe in JavaScript e il termine "metodo" è solo un termine colloquiale per una funzione che è un valore di una proprietà dell'oggetto. Ma quella funzione non ha un collegamento specifico al suo oggetto "contenente".
Considera il seguente esempio:
function Foo() {
this.data = 42,
document.body.onclick = this.method;
}
Foo.prototype.method = function() {
console.log(this.data);
};
La funzione this.methodè assegnata come gestore eventi click, ma se si document.bodyfa clic, il valore registrato sarà undefined, perché all'interno del gestore eventi, si thisriferisce a document.body, non all'istanza di Foo.
Come già accennato all'inizio, ciò che si thisriferisce dipende da come viene chiamata la funzione , non da come viene definita .
Se il codice fosse simile al seguente, potrebbe essere più ovvio che la funzione non ha un riferimento implicito all'oggetto:
function method() {
console.log(this.data);
}
function Foo() {
this.data = 42,
document.body.onclick = this.method;
}
Foo.prototype.method = method;
La soluzione è la stessa di cui sopra: se disponibile, utilizzare .bindper associare esplicitamente thisa un valore specifico
document.body.onclick = this.method.bind(this);
o chiama esplicitamente la funzione come "metodo" dell'oggetto, usando una funzione anonima come gestore di callback / evento e assegna l'oggetto ( this) a un'altra variabile:
var self = this;
document.body.onclick = function() {
self.method();
};
o usa una funzione freccia:
document.body.onclick = () => this.method();