Hvordan får man adgang til den korrekte `this` i en callback?

Jeg har en konstruktionsfunktion, som registrerer en hændelseshåndtering:

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);

Men jeg kan ikke få adgang til data-egenskaben for det oprettede objekt inden for callback'en. Det ser ud til, at this ikke henviser til det objekt, der blev oprettet, men til et andet objekt.

Jeg har også forsøgt at bruge en objektmetode i stedet for en anonym funktion:

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

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

men det udviser de samme problemer.

Hvordan kan jeg få adgang til det korrekte objekt?

Løsning

Hvad du bør vide om this

this (også kendt som "konteksten") er et særligt nøgleord i hver funktion, og dets værdi afhænger kun af hvordan funktionen blev kaldt, ikke hvordan/hvornår/hvor den blev defineret. Det er ikke påvirket af leksikale scopes som andre variabler (undtagen for pilefunktioner, se nedenfor). Her er nogle eksempler:

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`

Hvis du vil vide mere om this, kan du tage et kig på [MDN-dokumentationen][this].


Sådan henviser du til den korrekte this

Brug ikke this

Du ønsker faktisk ikke at få adgang til this i særdeleshed, men det objekt det refererer til. Derfor er en nem løsning blot at oprette en ny variabel, der også refererer til dette objekt. Variablen kan have et hvilket som helst navn, men almindelige navne er self og that.

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

Da self er en normal variabel, overholder den reglerne for leksikalsk rækkevidde og er tilgængelig inde i callbacken. Dette har også den fordel, at du kan få adgang til værdien this i selve callbacken.

Eksplicit indstilling af this i callback - del 1

Det kan se ud som om, at du ikke har nogen kontrol over værdien af this, fordi dens værdi sættes automatisk, men det er faktisk ikke tilfældet.

Hver funktion har metoden [.bind [docs]][bind], som returnerer en ny funktion med this bundet til en værdi. Funktionen har nøjagtig samme opførsel som den, du kaldte .bind på, blot er this sat af dig. Uanset hvordan eller hvornår funktionen kaldes, vil this altid henvise til den overgivne værdi.


function MyConstructor(data, transport) {
    this.data = data;
    var boundFunction = (function() { // parenthesis are not necessary
        alert(this.data);             // but might improve readability
    }).bind(this); // 
Kommentarer (17)

Det hele ligger i den "magiske" syntaks for at kalde en metode:

object.property();

Når du henter egenskaben fra objektet og kalder den på én gang, vil objektet være kontekst for metoden. Hvis du kalder den samme metode, men i separate trin, er konteksten i stedet det globale scope (vinduet):

var f = object.property;
f();

Når du får referencen til en metode, er den ikke længere knyttet til objektet, det er bare en reference til en almindelig funktion. Det samme sker, når du får referencen til brug som callback:

this.saveNextLevelData(this.setAll);

Det er der, hvor du binder konteksten til funktionen:

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

Hvis du bruger jQuery, skal du bruge metoden $.proxy i stedet, da bind ikke understøttes i alle browsere:

this.saveNextLevelData($.proxy(this.setAll, this));
Kommentarer (0)

Problemet med "kontekst"

Udtrykket "context" bruges nogle gange til at henvise til det objekt, der refereres til af this. Brugen er uhensigtsmæssig, fordi den hverken semantisk eller teknisk passer til ECMAScript's this.

"Kontekst" betyder de omstændigheder, der omgiver noget, som giver mening, eller nogle forudgående og efterfølgende oplysninger, som giver ekstra mening. Udtrykket "kontekst" bruges i ECMAScript til at henvise til udførelseskontekst, som er alle parametre, anvendelsesområde og this inden for anvendelsesområdet for en eller anden eksekverende kode.

Dette er vist i ECMA-262 afsnit 10.4.2:

Sæt ThisBinding til den samme værdi som ThisBinding i den kaldende udførelseskontekst

hvilket klart angiver, at Dette er en del af en udførelseskontekst.

En eksekveringskontekst giver de omgivende oplysninger, der giver mening til den kode, der udføres. Den indeholder meget mere information end blot thisBinding.

Så værdien af this er ikke "kontekst", det er blot en del af en eksekveringskontekst. Det er i bund og grund en lokal variabel, der kan sættes af opkaldet til et hvilket som helst objekt og i strict mode til en hvilken som helst værdi overhovedet.

Kommentarer (5)