Wie kann man innerhalb eines Rückrufs auf das richtige "this" zugreifen?

1425

Ich habe eine Konstruktorfunktion, die einen Ereignishandler 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 dataEigenschaft des erstellten Objekts im Rückruf zugreifen . Es sieht so aus, als würde thises sich nicht auf das erstellte Objekt beziehen, 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?

Felix Kling
quelle
95
Von Zeit zu Zeit habe ich eine bestimmte Frage so satt, dass ich mich entscheide, eine kanonische Antwort zu schreiben. Obwohl diese Fragen millionenfach beantwortet wurden, ist es nicht immer möglich, ein gutes Frage-Antwort-Paar zu finden, das nicht durch irrelevante Informationen „verschmutzt“ wird. Dies ist einer dieser Momente und eine dieser Fragen (und mir ist langweilig). Wenn Sie der Meinung sind, dass es tatsächlich eine gute kanonische Frage / Antwort für diese Art von Frage gibt, lassen Sie es mich wissen und ich werde diese löschen. Verbesserungsvorschläge sind willkommen!
Felix Kling
3
Nützliche TypeScript-Seite dazu , hauptsächlich auch für JS.
Ondra Žižka

Antworten:

1791

Was Sie wissen sollten this

this(auch bekannt als "der Kontext") ist ein spezielles Schlüsselwort in jeder Funktion und sein Wert hängt nur davon ab, wie die Funktion aufgerufen wurde, nicht davon, wie / wann / wo sie definiert wurde. Es wird nicht wie andere Variablen von lexikalischen Bereichen beeinflusst (mit Ausnahme der Pfeilfunktionen, siehe unten). 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`

Weitere thisInformationen finden Sie in der MDN-Dokumentation .


Wie man sich auf das Richtige bezieht this

Nicht benutzen this

Sie möchten eigentlich nicht thisspeziell auf das Objekt zugreifen , auf das es sich bezieht . Aus diesem Grund besteht eine einfache Lösung darin, einfach eine neue Variable zu erstellen, die sich auch auf dieses Objekt bezieht. Die Variable kann einen beliebigen Namen haben, aber gebräuchliche sind selfund that.

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

Da selfes sich um eine normale Variable handelt, befolgt sie die Regeln des lexikalischen Bereichs und ist innerhalb des Rückrufs zugänglich. Dies hat auch den Vorteil, dass Sie auf den thisWert des Rückrufs selbst zugreifen können .

Expliziter Satz thisdes Rückrufs - Teil 1

Es könnte so aussehen, als hätten Sie keine Kontrolle über den Wert von, thisda sein Wert automatisch festgelegt wird, aber das ist tatsächlich nicht der Fall.

Jede Funktion hat die Methode .bind [docs] , die eine neue Funktion mit thisan einen Wert gebundenem Wert zurückgibt . Die Funktion hat genau das gleiche Verhalten wie die von Ihnen aufgerufene .bind, nur dass diese thisvon Ihnen festgelegt wurde. Unabhängig davon, wie oder wann diese Funktion aufgerufen wird, thiswird immer auf den übergebenen Wert verwiesen.

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 Rückrufe thisan den Wert von MyConstructor's this.

Hinweis: Verwenden Sie beim Binden des Kontexts für jQuery stattdessen jQuery.proxy [docs] . Der Grund dafür ist, dass Sie den Verweis auf die Funktion nicht speichern müssen, wenn Sie einen Ereignisrückruf aufheben. jQuery erledigt das intern.

ECMAScript 6: Verwenden Sie Pfeilfunktionen

ECMAScript 6 führt Pfeilfunktionen ein , die als Lambda-Funktionen betrachtet werden können. Sie haben keine eigene thisBindung. Stattdessen thiswird im Gültigkeitsbereich genau wie bei einer normalen Variablen nachgeschlagen. Das heißt, Sie müssen nicht anrufen .bind. Dies 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));
}

Set thisdes Rückrufs - Teil 2

Einige Funktionen / Methoden, die Rückrufe akzeptieren, akzeptieren auch einen Wert, auf den sich die Rückrufe thisbeziehen sollten. Dies ist im Grunde dasselbe wie das Binden selbst, aber die Funktion / Methode erledigt es für Sie. Array#map [docs] ist eine solche Methode. Seine Unterschrift lautet:

array.map(callback[, thisArg])

Das erste Argument ist der Rückruf und das zweite Argument ist der Wert, thisauf den Bezug genommen werden soll. Hier ist ein erfundenes 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 für übergeben können oder nicht, thiswird normalerweise in der Dokumentation dieser Funktion / Methode angegeben. Zum Beispiel beschreibt die $.ajaxMethode [docs] von jQuery eine Option namens context:

Dieses Objekt wird zum Kontext aller Ajax-bezogenen Rückrufe.


Häufiges Problem: Verwenden von Objektmethoden als Rückruf- / Ereignishandler

Eine weitere häufige Manifestation dieses Problems ist die Verwendung einer Objektmethode als Rückruf- / Ereignishandler. Funktionen sind in JavaScript erstklassige Bürger, und der Begriff "Methode" ist nur ein umgangssprachlicher Begriff für eine Funktion, die ein Wert einer Objekteigenschaft ist. Diese Funktion hat jedoch keinen spezifischen Link zu ihrem "enthaltenden" 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.methodwird als Klickereignishandler zugewiesen. Wenn Sie jedoch auf klicken document.body, wird der Wert protokolliert undefined, da sich der Wert im Ereignishandler thisauf document.bodydie Instanz von und nicht auf die Instanz von bezieht Foo.
Wie bereits eingangs erwähnt this, hängt es davon ab, wie die Funktion aufgerufen wird und nicht davon, wie sie definiert ist .
Wenn der Code wie folgt lautet, ist es möglicherweise offensichtlicher, 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 dieselbe wie oben erwähnt: Wenn verfügbar, verwenden Sie .binddiese Option, um explizit thisan einen bestimmten Wert zu binden

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

oder rufen Sie die Funktion explizit als "Methode" des Objekts auf, indem Sie eine anonyme Funktion als Rückruf- / Ereignishandler verwenden und das Objekt ( this) einer anderen Variablen zuweisen :

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

oder verwenden Sie eine Pfeilfunktion:

document.body.onclick = () => this.method();
Felix Kling
quelle
39
Felix, ich habe diese Antwort schon einmal gelesen, aber nie geantwortet. Ich mache mir Sorgen, dass die Leute sie benutzen selfund sich thatdarauf beziehen this. Ich fühle mich so, weil thises sich um eine überladene Variable handelt, die in verschiedenen Kontexten verwendet wird. Entspricht selfnormalerweise der lokalen Instanz und thatbezieht sich normalerweise auf ein anderes Objekt. Ich weiß, dass Sie diese Regel nicht festgelegt haben, da ich sie an einer Reihe anderer Stellen gesehen habe, aber es ist auch der Grund, warum ich angefangen habe, sie zu verwenden _this, bin mir aber nicht sicher, wie andere sich fühlen, mit Ausnahme der uneinheitlichen Praxis das hat sich ergeben.
Vol7ron
3
@FelixKling Wäre es sicher anzunehmen, dass die Verwendung dieser Funktionen innerhalb des Prototyps immer das erwartete Verhalten aufweist, unabhängig davon, wie sie (normalerweise) aufgerufen werden? Gibt es bei der Verwendung von Rückrufen in Prototypfunktionen eine Alternative zu bind (), self oder so?
andig
5
@FelixKling Es kann manchmal nützlich sein, sich auf Function.prototype.call ()und zu verlassen Function.prototype.apply (). Besonders mit apply ()habe ich viel Kilometer gesammelt. Ich bin weniger geneigt, bind ()vielleicht nur aus Gewohnheit zu verwenden, obwohl ich mir bewusst (aber nicht sicher) bin, dass die Verwendung von Bind gegenüber den anderen Optionen geringfügige Overhead-Vorteile haben kann.
Nolo
5
Gute Antwort, aber erwägen Sie, eine zusätzliche optionale Lösung hinzuzufügen, die darin besteht, keine neuen oder überhaupt keine Klassen zu verwenden.
Aluan Haddad
4
Zu den Pfeilfunktionen "Stattdessen wird dies wie eine normale Variable im Gültigkeitsbereich nachgeschlagen." total diesen Klick für mich gemacht, danke! () => this.clicked();)
alphanumeric0101
211

Es gibt verschiedene Möglichkeiten, auf den übergeordneten Kontext im untergeordneten Kontext zuzugreifen:

  1. Sie können die bind()Funktion verwenden.
  2. Speichern Sie den Verweis auf context / this in einer anderen Variablen (siehe Beispiel unten).
  3. Verwenden Sie die ES6- Pfeilfunktionen .
  4. Alte Code / Funktion Design / Architektur - für das Sie das Kommando über haben sollten , Design Patterns in JavaScript.

1. Verwenden Sie die 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 context / this in 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);
  });
}
Mohan Dere
quelle
1
Die bind () Option ist erstaunlich, dass sie nur den Zeiger dieses Objekts übergibt, um dies auf dem anderen Objekt zu sein (: Danke!
Stav Bodik
Das bind () funktioniert wie ein Zauber. Vielen Dank +1 von mir :)
Anjana Silva
56

Es ist alles in der "magischen" Syntax des Aufrufs einer Methode:

object.property();

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

var f = object.property;
f();

Wenn Sie die Referenz einer Methode erhalten, ist diese nicht mehr an das Objekt angehängt, sondern nur eine Referenz auf eine einfache Funktion. Das gleiche passiert, wenn Sie die Referenz erhalten, die als Rückruf verwendet werden soll:

this.saveNextLevelData(this.setAll);

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

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

Wenn Sie jQuery verwenden, sollten Sie $.proxystattdessen die Methode verwenden, da dies bindnicht in allen Browsern unterstützt wird:

this.saveNextLevelData($.proxy(this.setAll, this));
Guffa
quelle
33

Das Problem mit "Kontext"

Der Begriff „Kontext“ wird manchmal verwendet , um das Objekt durch referenzierte bezieht dies . Seine Verwendung ist ungeeignet , da sie weder semantisch oder technisch mit passt ECMAScript ist dieser .

"Kontext" bezeichnet die Umstände, die etwas umgeben, das Bedeutung hinzufügt, oder einige vorhergehende und folgende Informationen, die zusätzliche Bedeutung verleihen. Der Begriff "Kontext" wird in ECMAScript verwendet, um sich auf den Ausführungskontext zu beziehen , bei dem es sich um alle Parameter, den Bereich und dies innerhalb des Bereichs eines ausgeführten Codes handelt.

Dies wird in Abschnitt 10.4.2 von ECMA-262 gezeigt :

Setzen Sie die ThisBinding auf denselben Wert wie die ThisBinding des aufrufenden Ausführungskontexts

Dies zeigt deutlich, dass dies Teil eines Ausführungskontexts ist.

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

Der Wert davon ist also nicht "Kontext", sondern nur ein Teil eines Ausführungskontexts. Es ist im Wesentlichen eine lokale Variable, die durch den Aufruf eines beliebigen Objekts und im strengen Modus auf einen beliebigen Wert gesetzt werden kann.

RobG
quelle
Kann dieser Antwort nicht zustimmen. Die Existenz des Begriffs "Ausführungskontext" verbietet andere Verwendungen von "Kontext" ebenso wenig wie andere Verwendungen von "Ausführung". Vielleicht gibt es einen besseren Begriff zu beschreiben, thisaber hier wird keiner angeboten, und es ist wohl zu spät, die Tür zum "Kontext" zu schließen.
Roamer-1888
@ Roamer-1888 - Vielen Dank für die Bearbeitung. Sie haben Recht, aber mein Argument beruht nicht auf der Existenz eines "Ausführungskontexts", der den "Kontext" für einen anderen Zweck ausschließt. Vielmehr basiert es darauf, dass "Kontext" sowohl aus technischer als auch aus semantischer Sicht unangemessen ist. Ich denke auch, dass die Verwendung von "Kontext" anstelle von "dies" aussterben wird. Ich sehe keinen Grund, einen alternativen Begriff zu diesem oder jenem zu finden. Bindung , er verschleiert nur und bedeutet , dass Sie irgendwann erklären müssen, dass "Kontext" tatsächlich dies ist und dass es sich sowieso nicht um "Kontext" handelt. :-)
RobG
Ich glaube nicht, dass Sie sagen können, dass dies in keiner Weise "Kontext" ist, wenn Sie bereits zugegeben haben, dass es ein Teil eines Ausführungskontexts ist, in dem "Ausführung" lediglich ein Adjektiv ist.
Roamer-1888
@ Roamer-1888 - Ich werde dieses Gespräch über diesen Punkt hinaus nicht fortsetzen. Ja, dies ist Teil eines Ausführungskontexts. Zu sagen, dass es der Kontext ist, ist wie zu sagen, dass ein Spieler eines Teams das Team ist.
RobG
RobG, schade, dass du nicht weitermachen willst. Es ist eine interessante Debatte. Danke, dass du mir deine Zeit gegeben hast.
Roamer-1888
31

Sie sollten über "dieses" Schlüsselwort Bescheid wissen.

Meiner Ansicht nach können Sie "dies" auf drei Arten implementieren (Selbst- / Pfeilfunktion / Bindungsmethode)

Dieses Schlüsselwort einer Funktion verhält sich in JavaScript etwas anders als in anderen Sprachen.

Es gibt auch einige Unterschiede zwischen dem strengen und dem nicht strengen Modus.

In den meisten Fällen wird der Wert davon bestimmt, wie eine Funktion aufgerufen wird.

Sie kann während der Ausführung nicht durch Zuweisung festgelegt werden und kann bei jedem Aufruf der Funktion unterschiedlich sein.

ES5 führte die bind () -Methode ein, um den Wert einer Funktion festzulegen, unabhängig davon, wie sie aufgerufen wird.

und ES2015 führten Pfeilfunktionen ein, die diese Bindung nicht selbst bereitstellen (sie behält diesen Wert des einschließenden lexikalischen Kontexts bei).

Methode 1: Selbst - Selbst wird verwendet, um einen Verweis auf das Original beizubehalten, selbst wenn sich der Kontext ändert. Diese Technik wird häufig in Event-Handlern verwendet (insbesondere bei Verschlüssen).

Referenz : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this

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

Methode 2 : Pfeilfunktion - Ein Pfeilfunktionsausdruck ist eine syntaktisch kompakte Alternative zu einem regulären Funktionsausdruck.

obwohl ohne eigene Bindungen an this, Argumente, super oder new.target-Schlüsselwörter.

Pfeilfunktionsausdrücke sind als Methoden schlecht geeignet und können nicht als Konstruktoren verwendet werden.

Referenz : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions

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

Methode3 : Bind- Die bind () -Methode erstellt eine neue Funktion, die,

Wenn dieses Schlüsselwort aufgerufen wird, wird es auf den angegebenen Wert gesetzt.

mit einer bestimmten Folge von Argumenten vor allen, die beim Aufruf der neuen Funktion angegeben werden.

Referenz: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Function/bind

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

Zunächst müssen Sie ein klares Verständnis scopeund Verhalten von thisKeywords im Kontext von haben scope.

this& scope:


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

   1) Global Scope

   2) Function Scope

Kurz gesagt, der globale Bereich bezieht sich auf das Fensterobjekt. Auf in einem globalen Bereich deklarierte Variablen kann von überall aus zugegriffen werden. Andererseits befindet sich der Funktionsbereich innerhalb einer Funktion. Auf die innerhalb einer Funktion deklarierte Variable kann normalerweise nicht von außerhalb der Welt zugegriffen werden. thisDas Schlüsselwort im globalen Bereich bezieht sich auf das Fensterobjekt. thisDie Funktion inside bezieht sich auch auf das Fensterobjekt. So thiswird immer auf das Fenster verwiesen, bis wir eine Möglichkeit finden this, einen Kontext unserer Wahl zu manipulieren .

--------------------------------------------------------------------------------
-                                                                              -
-   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 zur Manipulation von thisRückruffunktionen:

Hier habe ich eine Konstruktorfunktion namens Person. Es hat eine Eigenschaft namens nameund vier Verfahren genannt sayNameVersion1, sayNameVersion2, sayNameVersion3, sayNameVersion4. Alle vier haben eine bestimmte Aufgabe. Akzeptieren Sie einen Rückruf und rufen Sie ihn auf. Der Rückruf hat eine bestimmte Aufgabe, die darin besteht, die Eigenschaft name einer Instanz der Person-Konstruktorfunktion 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)

}

Erstellen wir nun eine Instanz aus dem Personenkonstruktor und rufen verschiedene Versionen der sayNameVersionXMethode (X bezieht sich auf 1,2,3,4) auf, um niceCallbackzu sehen, auf wie viele Arten wir den thisinternen Rückruf bearbeiten können, um auf die personInstanz zu verweisen .

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

binden :

Mit bind wird eine neue Funktion erstellt, bei der das thisSchlüsselwort auf den angegebenen Wert gesetzt ist.

sayNameVersion1und sayNameVersion2verwenden Sie bind, um thisdie Rückruffunktion zu manipulieren .

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

Der erste thisRückruf wird mit einem Rückruf innerhalb der Methode selbst gebunden. Der zweite Rückruf 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 :

Die first argumentof- callMethode wird thisinnerhalb der Funktion verwendet, mit der aufgerufen wirdcall angehängten wird.

sayNameVersion3wird verwendet call, um das zu 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)

anwenden :

Ähnlich bezieht sich das callerste Argument von applyauf das Objekt, das durch angezeigt wirdthis Schlüsselwort .

sayNameVersion4wird applyzum Manipulieren verwendet this, um auf ein Personenobjekt zu verweisen

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

und es wird wie folgt aufgerufen. Einfach der Rückruf wird übergeben,

p1.sayNameVersion4(niceCallback)
AL-Zami
quelle
1
Jede konstruktive Kritik bezüglich der Antwort wird geschätzt!
AL-Zami
1
Das Schlüsselwort this im globalen Bereich bezieht sich nicht unbedingt auf das Fensterobjekt . Das gilt nur in einem Browser.
Randall Flagg
1
@RandallFlagg i schrieb diese Antwort von einem perspective.Fell freien Browser diese Antwort inhance wenn nötig :)
AL-Zami
19

Wir können dies nicht binden setTimeout(), da es immer mit einem globalen Objekt (Fenster) ausgeführt wird. Wenn Sie thisin der Rückruffunktion auf den Kontext zugreifen möchten, können Sie mit bind()der Rückruffunktion Folgendes erreichen:

setTimeout(function(){
    this.methodName();
}.bind(this), 2000);
Datta Chanewad
quelle
9
Wie unterscheidet sich das von den vorhandenen Antworten?
Felix Kling
13

Die Frage dreht sich darum, wie sich das thisSchlüsselwort in Javascript verhält. thisverhält sich anders als unten,

  1. Der Wert von this wird normalerweise durch einen Funktionsausführungskontext bestimmt.
  2. thisBezieht sich im globalen Bereich auf das globale Objekt (das windowObjekt).
  3. Wenn Strict - Modus für jede Funktion aktiviert ist , wird der Wert thissein wird undefinedals im Strict - Modus bezieht sich global Objekt undefinedanstelle derwindow Objekts.
  4. Das Objekt, das vor dem Punkt steht, ist das, an das dieses Schlüsselwort gebunden wird.
  5. Wir können den Wert davon explizit mit einstellen call(),bind() undapply()
  6. Wenn die new Schlüsselwort (ein Konstruktor) verwendet wird, ist dies an das neu erstellte Objekt gebunden.
  7. Pfeilfunktionen binden nicht this - stattdessen werden thissie lexikalisch gebunden (dh basierend auf dem ursprünglichen Kontext)

Wie die meisten Antworten vermuten lassen, können wir die Pfeilfunktion oder die bind()Methode oder Self var verwenden. Ich würde einen Punkt über Lambdas (Pfeilfunktion) aus dem Google JavaScript Style Guide zitieren

Verwenden Sie lieber Pfeilfunktionen als f.bind (this) und insbesondere goog.bind (f, this). Vermeiden Sie es, const self = this zu schreiben. Pfeilfunktionen sind besonders nützlich für Rückrufe, die manchmal unerwartete zusätzliche Argumente übergeben.

Google empfiehlt eindeutig, Lambdas anstelle von bind oder zu verwenden const self = this

Die beste Lösung wäre also, Lambdas wie folgt zu verwenden:

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

Verweise:

  1. https://medium.com/tech-tajawal/javascript-this-4-rules-7354abdb274c
  2. Pfeil-Funktionen-gegen-Bindung
Code_Mode
quelle
In dieser Frage geht es speziell um die Verwendung von Funktionen / Methoden als Rückruf. Ihre Antwort passt möglicherweise besser zu stackoverflow.com/q/3127429/218196 .
Felix Kling
@FelixKling Ja, bei der Frage geht es um die Verwendung von Funktionen / Methoden als Rückrufe in diesem Hauptproblem. thisDies lag an der Behandlung des Schlüsselworts. Deshalb habe ich meine Antwort in zwei Teile geteilt, einen über thisund einen über die Verwendung von Funktionen / Methoden als Rückruf. Fühlen Sie sich frei, die Antwort zu bearbeiten.
Code_Mode
Ich finde Ihren vierten Punkt mehrdeutig formuliert. Betrachten Sie das Beispiel „Problem bei der Verwendung von Methoden mit diesem Objekt als Rückruf“ , bei dem das richtige Objekt vor dem Punkt steht, der Kontext jedoch nicht dieses Objekt ist.
bleistift2
7

Derzeit ist ein anderer Ansatz möglich, wenn Klassen im Code verwendet werden.

Mit Unterstützung von Klassenfeldern ist es möglich, den nächsten Weg zu gehen:

class someView {
    onSomeInputKeyUp = (event) => {
        console.log(this); // this refers to correct value
    // ....
    someInitMethod() {
        //...
        someInput.addEventListener('input', this.onSomeInputKeyUp)

Sicherlich ist unter der Haube alles alte gute Pfeilfunktion, die den Kontext bindet, aber in dieser Form sieht es viel klarer aus als die explizite Bindung.

Da es sich um einen Vorschlag für Stufe 3 handelt, benötigen Sie Babel und ein entsprechendes Babel-Plugin , um es wie bisher zu verarbeiten (08/2018).

Skyboyer
quelle
2
Genau so habe ich es in Typescript zum Laufen gebracht: public methodName = (params) => { body }innerhalb einer Klasse.
Yeyeyerman
5

Ein anderer Ansatz, der seit DOM2 die Standardmethode zum Binden thisinnerhalb des Ereignis-Listeners ist, mit dem Sie den Listener (unter anderem) immer entfernen können, ist die handleEvent(evt)Methode von der EventListenerSchnittstelle:

var obj = {
  handleEvent(e) {
    // always true
    console.log(this === obj);
  }
};

document.body.addEventListener('click', obj);

Detaillierte Informationen zur Verwendung handleEventfinden Sie hier: https://medium.com/@WebReflection/dom-handleevent-a-cross-platform-standard-since-year-2000-5bf17287fd38

Andrea Puddu
quelle
0

this in JS:

Der Wert von thisin JS wird zu 100% dadurch bestimmt, wie eine Funktion aufgerufen wird und nicht wie sie definiert wird. Wir können den Wert von relativ einfach thisdurch die 'Linke der Punktregel' finden :

  1. Wenn Funktionen mit dem Funktionsschlüsselwort erstellt werden, wird der Wert von this das Objekt links vom Punkt der aufgerufenen Funktion
  2. Wenn vom Punkt kein Objekt mehr vorhanden ist, ist der Wert thisinnerhalb einer Funktion häufig das globale Objekt ( globalim Knoten, windowim Browser). Ich würde nicht empfehlen, das thisSchlüsselwort hier zu verwenden, da es weniger explizit ist als die Verwendung von so etwaswindow !
  3. Es gibt bestimmte Konstrukte wie Pfeilfunktionen und Funktionen, die mit dem erstellt wurden Function.prototype.bind() Funktion der Wert von festgelegt werden kann this. Dies sind Ausnahmen von der Regel, aber sie sind sehr hilfreich, um den Wert von festzulegen this.

Beispiel in nodeJS

module.exports.data = 'module data';
// This outside a function in node refers to module.exports object
console.log(this);

const obj1 = {
    data: "obj1 data",
    met1: function () {
        console.log(this.data);
    },
    met2: () => {
        console.log(this.data);
    },
};

const obj2 = {
    data: "obj2 data",
    test1: function () {
        console.log(this.data);
    },
    test2: function () {
        console.log(this.data);
    }.bind(obj1),
    test3: obj1.met1,
    test4: obj1.met2,
};

obj2.test1();
obj2.test2();
obj2.test3();
obj2.test4();
obj1.met1.call(obj2);

Ausgabe:

Geben Sie hier die Bildbeschreibung ein

Lassen Sie sich 1: 1 durch die Ausgänge führen (wobei das erste Protokoll ab dem zweiten ignoriert wird):

  1. thisist obj2wegen der links vom Punkt Regel können wir sehen , wie test1genannt wirdobj2.test1(); . obj2ist links vom Punkt und damit derthis Wert .
  2. Auch wenn obj2links vom Punkt, test2wird obj1über die bind()Methode gebunden . Also diethis Wert ist alsoobj1 .
  3. obj2 ist links vom Punkt von der Funktion, die aufgerufen wird: obj2.test3() . Daher obj2wird der Wert von seinthis .
  4. In diesem Fall: obj2.test4() obj2ist links vom Punkt. Die Pfeilfunktion hat jedoch keine eigenethis Bindung. Daher wird es an den thisWert des äußeren Bereichs gebunden, der der istmodule.exports Objekt ist, das am Anfang protokolliert wurde.
  5. Wir können auch den Wert von angeben this von auch mithilfe der callFunktion . Hier können wir den gewünschten thisWert als Argument übergeben, was obj2in diesem Fall der Fall ist.
Willem van der Veen
quelle