Imam funkcijo konstruktorja, ki registrira izvajalca dogodka:
-- begin snippet: js hide: false -->
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);
Vendar znotraj povratnega klica ne morem dostopati do lastnosti data
ustvarjenega predmeta. Izgleda, da se this
ne nanaša na objekt, ki je bil ustvarjen, ampak na drug objekt.
Poskušal sem tudi uporabiti metodo objekta namesto anonimne funkcije:
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', this.alert);
}
MyConstructor.prototype.alert = function() {
alert(this.name);
};
vendar ima enake težave.
Kako lahko dostopam do pravilnega predmeta?
this
this
(tudi "kontekst") je posebna ključna beseda znotraj vsake funkcije, njena vrednost pa je odvisna le od tega, kako je bila funkcija poklicana, ne pa od tega, kako/kdaj/kje je bila definirana. Nanjo ne vplivajo leksikalni obsegi kot na druge spremenljivke (razen pri puščičnih funkcijah, glej spodaj). Tukaj je nekaj primerov:
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
, si oglejte dokumentacijo MDN.this
this
Pravzaprav ne želite dostopati do this
, temveč do objekta, na katerega se nanaša. Zato je preprosta rešitev, da preprosto ustvarite novo spremenljivko, ki se prav tako nanaša na ta objekt. Spremenljivka ima lahko poljubno ime, vendar sta običajni imeni self
in that
.
function MyConstructor(data, transport) {
this.data = data;
var self = this;
transport.on('data', function() {
alert(self.data);
});
}
Ker je self
običajna spremenljivka, upošteva pravila leksikalnega obsega in je dostopna znotraj povratnega klica. Prednost tega je tudi, da lahko dostopate do vrednosti this
v samem povratnem klicu.
this
povratnega klica - 1. delMorda se zdi, da nimate nadzora nad vrednostjo this
, saj je njena vrednost nastavljena samodejno, vendar temu v resnici ni tako.
Vsaka funkcija ima metodo .bind
[docs], ki vrne novo funkcijo z vrednostjo this
, vezano na vrednost. Funkcija se obnaša popolnoma enako kot tista, ki ste jo klicali .bind
, le da ste this
določili vi. Ne glede na to, kako ali kdaj se ta funkcija pokliče, se bo this
vedno skliceval na posredovano vrednost.
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);
}
V tem primeru vežemo this
povratnega klica na vrednost MyConstructorja
this. **Note:** Pri vezavi konteksta za jQuery namesto tega uporabite [
jQuery.proxy` [docs]]proxy. Razlog za to je, da vam ni treba shraniti reference na funkcijo, ko razvezujete povratni klic dogodka. jQuery to opravi interno.
ECMAScript 6 uvaja funkcije strel, ki si jih lahko predstavljamo kot funkcije lambda. Nimajo lastne vezave this
. Namesto tega se this
išče v obsegu kot običajna spremenljivka. To pomeni, da vam ni treba klicati .bind
. To ni edino posebno vedenje, ki ga imajo, za več informacij si oglejte dokumentacijo MDN.
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', () => alert(this.data));
}
this
povratnega klica - 2. delNekatere funkcije/metode, ki sprejemajo povratne klice, sprejemajo tudi vrednost, na katero se mora nanašati this
povratnega klica. To je v bistvu enako, kot če bi jo vezali sami, vendar funkcija/metoda to stori namesto vas. Array#map
[docs] je taka metoda. Njen podpis je:
array.map(callback[, thisArg])
Prvi argument je povratni klic, drugi argument pa je vrednost, na katero se mora nanašati this
. Tukaj je izmišljen primer:
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
this
posredujete vrednost, je običajno navedeno v dokumentaciji te funkcije/metode. Na primer, jQueryjeva metoda $.ajax
[docs] opisuje možnost, imenovano context
:
Ta objekt bo postal kontekst vseh povratnih klicev, povezanih z Ajaxom.Druga pogosta pojavna oblika te težave je, ko se objektna metoda uporabi kot povratni klic/obvladovalec dogodka. Funkcije so prvovrstni državljani v jeziku JavaScript in izraz "metoda" je le pogovorni izraz za funkcijo, ki je vrednost lastnosti objekta. Vendar ta funkcija nima posebne povezave s svojim objektom, ki jo vsebuje. Upoštevajte naslednji primer:
function Foo() {
this.data = 42,
document.body.onclick = this.method;
}
Foo.prototype.method = function() {
console.log(this.data);
};
Funkcija this.method
je dodeljena kot izvajalec dogodka klik, vendar bo ob kliku na document.body
prijavljena vrednost neopredeljena
, saj se znotraj izvajalca dogodka this
nanaša na document.body
in ne na primerek Foo
.
Kot je bilo omenjeno že na začetku, je to, na kaj se this
nanaša, odvisno od tega, kako je funkcija poklicana, in ne od tega, kako je opredeljena.
Če bi bila koda podobna naslednji, bi bilo morda bolj očitno, da funkcija nima implicitne reference na objekt:
function method() {
console.log(this.data);
}
function Foo() {
this.data = 42,
document.body.onclick = this.method;
}
Foo.prototype.method = method;
Rešitev je enaka, kot je navedeno zgoraj: Če je na voljo, uporabite .bind
za izrecno vezavo this
na določeno vrednost
document.body.onclick = this.method.bind(this);
ali izrecno pokličite funkcijo kot "metodo" objekta, tako da uporabite anonimno funkcijo kot povratni klic / izvajalca dogodka in dodelite objekt (this
) drugi spremenljivki:
var self = this;
document.body.onclick = function() {
self.method();
};
ali uporabite funkcijo puščica:
document.body.onclick = () => this.method();
Vse je v "čarobni" sintaksi klicanja metode:
object.property();
Ko pridobite lastnost iz predmeta in jo pokličete v enem zamahu, bo predmet kontekst za metodo. Če kličete isto metodo, vendar v ločenih korakih, je namesto tega kontekst globalno področje (okno):
var f = object.property;
f();
Ko dobite referenco na metodo, ta ni več vezana na objekt, temveč je samo referenca na navadno funkcijo. Enako se zgodi, ko dobite referenco, ki jo želite uporabiti kot povratni klic:
this.saveNextLevelData(this.setAll);
Tu bi kontekst povezali s funkcijo:
this.saveNextLevelData(this.setAll.bind(this));
Če uporabljate jQuery, morate namesto tega uporabiti metodo $.proxy
, saj bind
ni podprt v vseh brskalnikih:
this.saveNextLevelData($.proxy(this.setAll, this));
Izraz "kontekst" se včasih uporablja za objekt, na katerega se sklicuje to. Njegova uporaba je neprimerna, ker se ne ujema niti semantično niti tehnično s ECMAScript's this.
"Kontekst" pomeni okoliščine, ki nekaj obkrožajo in dodajajo pomen, ali nekatere predhodne in naslednje informacije, ki dajejo dodaten pomen. Izraz "kontekst" se v ECMAScriptu uporablja za kontekst izvajanja, kar so vsi parametri, področje uporabe in this znotraj področja uporabe neke izvajane kode.
To je prikazano v ECMA-262, oddelek 10.4.2:
Nastavite ThisBinding na enako vrednost kot ThisBinding v kličočega konteksta izvajanja
kar jasno nakazuje, da je this del konteksta izvajanja.
Kontekst izvajanja zagotavlja okoliške informacije, ki dodajo pomen kodi, ki se izvaja. Vključuje veliko več informacij kot le thisBinding.
Torej vrednost this ni "kontekst", ampak je le del konteksta izvajanja. V bistvu'je lokalna spremenljivka, ki jo lahko klic nastavi na katerikoli objekt in v strogem načinu na katerokoli vrednost.