Frage Wie kann man innerhalb eines Callbacks auf das richtige `This 'zugreifen?


Ich habe eine Konstruktorfunktion, die einen Event-Handler registriert:

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

Ich kann jedoch nicht auf die data Eigenschaft des erstellten Objekts innerhalb des Callbacks. Es sieht aus wie this bezieht sich nicht auf das Objekt, das erstellt wurde, sondern auf ein anderes.

Ich habe auch versucht, eine Objektmethode anstelle einer anonymen Funktion zu verwenden:

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

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

aber es zeigt die gleichen Probleme.

Wie kann ich auf das richtige Objekt zugreifen?


914
2017-11-29 06:13


Ursprung


Antworten:


Was du wissen solltest this

this (aka "the context") ist ein spezielles Schlüsselwort innerhalb jeder Funktion und ihr Wert hängt nur davon ab Wie Die Funktion wurde aufgerufen, nicht wie / wann / wo sie definiert wurde. Es ist nicht von lexikalischen Bereichen betroffen, wie andere Variablen auch. Hier sind einige Beispiele:

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`

Mehr darüber lernen thisSieh dir die. an MDN-Dokumentation.


Wie man sich auf das richtige bezieht this

Benutze es nicht this

Sie wollen eigentlich nicht zugreifen this insbesondere aber das Objekt, auf das es sich bezieht. Aus diesem Grund besteht eine einfache Lösung darin, einfach eine neue Variable zu erstellen, die ebenfalls auf dieses Objekt verweist. Die Variable kann einen beliebigen Namen haben, aber gebräuchliche Namen sind self und that.

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

Schon seit self ist eine normale Variable, sie folgt lexikalischen Bereichsregeln und ist innerhalb des Callbacks zugänglich. Dies hat auch den Vorteil, dass Sie auf die zugreifen können this Wert des Callbacks selbst.

Explizit gesetzt this des Rückrufs - Teil 1

Es sieht so aus, als hätten Sie keine Kontrolle über den Wert von this weil sein Wert automatisch gesetzt wird, aber das ist tatsächlich nicht der Fall.

Jede Funktion hat die Methode .bind  [Dokumente], die eine neue Funktion mit zurückgibt this an einen Wert gebunden. Die Funktion hat genau das gleiche Verhalten wie das, das Sie aufgerufen haben .bind auf, nur das this wurde von dir eingestellt. Egal wie oder wann diese Funktion aufgerufen wird, this bezieht sich immer auf den übergebenen Wert.

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 diesem Fall binden wir die Callbacks this zum Wert von MyConstructorist es this.

Hinweis: Verwenden Sie beim Bindungskontext für jQuery jQuery.proxy  [Dokumente] stattdessen. Der Grund dafür ist, dass Sie den Verweis auf die Funktion nicht speichern müssen, wenn Sie einen Ereignis-Callback aufheben. jQuery behandelt das intern.

ECMAScript 6: Verwenden Pfeilfunktionen

ECMAScript 6 führt ein Pfeilfunktionen, die man sich als Lambda-Funktionen vorstellen kann. Sie haben keine eigenen this Bindung. Stattdessen, this wird im Umfang genau wie eine normale Variable nachgeschlagen. Das heißt, Sie müssen nicht anrufen .bind. Das ist nicht das einzige spezielle Verhalten, das sie haben. Weitere Informationen finden Sie in der MDN-Dokumentation.

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

einstellen this des Rückrufs - Teil 2

Einige Funktionen / Methoden, die Rückrufe akzeptieren, akzeptieren auch einen Wert, auf den der Rückruf zurückgeht this sollte sich beziehen auf. Dies ist im Prinzip das Gleiche wie Sie es selbst binden, aber die Funktion / Methode macht es für Sie. Array#map  [Dokumente] ist eine solche Methode. Seine Unterschrift ist:

array.map(callback[, thisArg])

Das erste Argument ist der Rückruf und das zweite Argument ist der Wert this sollte sich beziehen auf. Hier ist ein künstliches Beispiel:

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

Hinweis: Ob Sie einen Wert übergeben können oder nicht this wird normalerweise in der Dokumentation dieser Funktion / Methode erwähnt. Beispielsweise, jQuery's $.ajax Methode [Dokumente] beschreibt eine Option namens context:

Dieses Objekt wird zum Kontext aller Ajax-bezogenen Callbacks gemacht.


Häufiges Problem: Objektmethoden als Callbacks / Eventhandler verwenden

Eine andere häufige Manifestation dieses Problems ist, wenn eine Objektmethode als Callback / Event-Handler verwendet wird. Funktionen sind erstklassige Bürger in JavaScript und der Begriff "Methode" ist nur ein umgangssprachlicher Begriff für eine Funktion, die ein Wert einer Objekteigenschaft ist. Diese Funktion hat jedoch keine spezifische Verknüpfung zu ihrem "containing" -Objekt.

Betrachten Sie das folgende Beispiel:

function Foo() {
    this.data = 42,
    document.body.onclick = this.method;
}

Foo.prototype.method = function() {
    console.log(this.data);
};

Die Funktion this.method wird als Click Event Handler zugewiesen, aber wenn der document.body wird geklickt, der Wert wird protokolliert undefined, weil innerhalb des Event-Handlers, this bezieht sich auf document.body, nicht die Instanz von Foo.
Wie schon eingangs erwähnt, was this bezieht sich darauf, wie die Funktion ist namensnicht wie es ist definiert.
Wenn der Code dem folgenden ähnelte, ist es offensichtlich, dass die Funktion keinen impliziten Verweis auf das Objekt hat:

function method() {
    console.log(this.data);
}


function Foo() {
    this.data = 42,
    document.body.onclick = this.method;
}

Foo.prototype.method = method;

Die Lösung ist das gleiche wie oben erwähnt: Wenn verfügbar, verwenden Sie .bind explizit binden this zu einem bestimmten Wert

document.body.onclick = this.method.bind(this);

oder rufen Sie die Funktion explizit als "Methode" des Objekts auf, indem Sie eine anonyme Funktion als Callback / Event-Handler verwenden und das Objekt zuweisen (this) zu einer anderen Variablen:

var self = this;
document.body.onclick = function() {
    self.method();
};

oder benutze eine Pfeilfunktion:

document.body.onclick = () => this.method();

1206
2017-11-29 06:13



Es gibt mehrere Möglichkeiten, auf den übergeordneten Kontext im Kindkontext zuzugreifen:

  1. Sie können verwenden binden() Funktion.
  2. Speichern Sie den Verweis auf den Kontext / das innerhalb einer anderen Variablen (siehe unten).
  3. Verwenden Sie ES6 Pfeil Funktionen.
  4. Ändern Sie den Code / das Funktionsdesign / die Architektur - hierfür sollten Sie eine Befehlsübergabe durchführen Designmuster in Javascript.

1. Verwenden bind() Funktion

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

Wenn Sie verwenden underscore.js - http://underscorejs.org/#bind 

transport.on('data', _.bind(function () {
    alert(this.data);
}, this));

2 Speichern Sie den Verweis auf den Kontext / dies innerhalb einer anderen Variablen

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

3 Pfeilfunktion

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

139
2017-08-13 10:26



Es ist alles in der "magischen" Syntax, eine Methode aufzurufen:

object.property();

Wenn Sie die Eigenschaft aus dem Objekt abrufen und sie auf einmal aufrufen, ist das Objekt der Kontext für die Methode. Wenn Sie die gleiche Methode, aber in getrennten Schritten aufrufen, ist der Kontext stattdessen der globale Bereich (Fenster):

var f = object.property;
f();

Wenn Sie die Referenz einer Methode erhalten, ist sie nicht mehr an das Objekt gebunden, sondern lediglich eine Referenz auf eine einfache Funktion. Das gleiche passiert, wenn Sie die Referenz als Rückruf erhalten:

this.saveNextLevelData(this.setAll);

Dort würden Sie den Kontext an die Funktion binden:

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

Wenn Sie jQuery verwenden, sollten Sie das verwenden $.proxy Methode stattdessen, als bind wird nicht in allen Browsern unterstützt:

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

35
2018-05-21 00:11



Das Problem mit "Kontext"

Der Begriff "Kontext" wird manchmal verwendet, um sich auf das Objekt zu beziehen, auf das verwiesen wird Dies. Es ist unpassend, weil es nicht semantisch oder technisch mit passt ECMAScript Dies.

"Kontext" bedeutet die Umstände, die etwas umgeben, das Bedeutung hinzufügt, oder eine vorausgehende und folgende Information, die eine zusätzliche Bedeutung gibt. Der Begriff "Kontext" wird in ECMAScript verwendet, um darauf Bezug zu nehmen Ausführungskontext, das sind alle Parameter, Umfang und Dies im Rahmen eines Ausführungscodes.

Dies wird in gezeigt ECMA-262 Abschnitt 10.4.2:

Setzen Sie ThisBinding auf denselben Wert wie ThisBinding des   Ausführungskontext aufrufen

was deutlich darauf hinweist Dies ist Teil eines Ausführungskontextes.

Ein Ausführungskontext stellt die umgebenden Informationen bereit, die dem ausgeführten Code Bedeutung verleihen. Es enthält viel mehr Informationen als nur die dieseBindung.

Also der Wert von Dies ist nicht "context", es ist nur ein Teil eines Ausführungskontexts. Es ist im Wesentlichen eine lokale Variable, die durch den Aufruf eines beliebigen Objekts und im strikten Modus auf einen beliebigen Wert gesetzt werden kann.


20
2018-06-01 00:44



Zuerst müssen Sie ein klares Verständnis davon haben scope und Verhalten von this Stichwort im Kontext von scope.

this & scope :


there are two types of scope in javascript. They are :

   1) Global Scope

   2) Function Scope

Kurz gesagt, der globale Gültigkeitsbereich bezieht sich auf das Fensterobjekt. Variablen, die in einem globalen Gültigkeitsbereich deklariert sind, sind von überall zugänglich. Andererseits befindet sich der Funktionsumfang innerhalb einer Funktion. In einer Funktion deklarierte Variablen können normalerweise nicht von außen bezogen werden.this Schlüsselwort im globalen Bereich bezieht sich auf das Fensterobjekt.this Inside-Funktion bezieht sich auch auf das Fensterobjekt. So this bezieht sich immer auf das Fenster, bis wir einen Weg zur Manipulation finden this um einen Kontext unserer eigenen Wahl anzuzeigen.

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

Verschiedene Möglichkeiten zu manipulieren this interne Rückruffunktionen:

Hier habe ich eine Konstruktorfunktion namens Person. Es hat eine Eigenschaft namens name und vier Methoden genannt sayNameVersion1,sayNameVersion2,sayNameVersion3,sayNameVersion4. Alle vier von ihnen haben eine bestimmte Aufgabe. Akzeptieren Sie einen Rückruf und rufen Sie ihn auf. Der Rückruf hat eine spezifische Aufgabe, nämlich die Namenseigenschaft einer Instanz der Konstruktorfunktion von Person zu protokollieren.

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)

}

Lassen Sie uns jetzt eine Instanz von person constructor erstellen und verschiedene Versionen von aufrufen sayNameVersionX (X bezieht sich auf 1,2,3,4) Methode mit niceCallback um zu sehen, auf wie viele Arten wir das manipulieren können this innerhalb des Rückrufs, um auf das zu verweisen person Beispiel.

var p1 = new Person('zami') // create an instance of Person constructor

binden : 

Was bind ist, ist eine neue Funktion mit dem erstellen this Schlüsselwort auf den angegebenen Wert gesetzt.

sayNameVersion1 und sayNameVersion2 benutze bind um zu manipulieren this der Rückruffunktion.

this.sayNameVersion1 = function(callback){
    callback.bind(this)()
}
this.sayNameVersion2 = function(callback){
    callback()
}

erste Bindung this mit Callback innerhalb der Methode selbst. Und für den zweiten Callback wird mit dem daran gebundenen Objekt übergeben.

p1.sayNameVersion1(niceCallback) // pass simply the callback and bind happens inside the sayNameVersion1 method

p1.sayNameVersion2(niceCallback.bind(p1)) // uses bind before passing callback

Anruf : 

Das first argument des call Methode wird als verwendet this innerhalb der Funktion, die aufgerufen wird callangehängt an.

sayNameVersion3 Verwendet call manipulieren this um auf das von uns erstellte Personenobjekt anstelle des Fensterobjekts zu verweisen.

this.sayNameVersion3 = function(callback){
    callback.call(this)
}

und es heißt wie folgt:

p1.sayNameVersion3(niceCallback)

sich bewerben : 

Ähnlich zu callerstes Argument von apply bezieht sich auf das Objekt, das angezeigt werden soll this Stichwort.

sayNameVersion4 Verwendet apply manipulieren this auf Personenobjekt verweisen

this.sayNameVersion4 = function(callback){
    callback.apply(this)
}

und es heißt wie folgt. Einfach der Rückruf wird übergeben,

p1.sayNameVersion4(niceCallback)

15
2017-08-18 17:58



Daran können wir nicht binden setTimeout(), wie es immer mit ausführen globales Objekt (Fenster), wenn Sie zugreifen möchten this Kontext in der Callback-Funktion dann mit bind() zur Callback-Funktion können wir erreichen als:

setTimeout(function(){
    this.methodName();
}.bind(this), 2000);

9
2017-11-17 14:32