it-swarm.com.de

Wie kann auf das richtige "this" innerhalb eines Rückrufs zugegriffen werden?

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 data-Eigenschaft des erstellten Objekts im Callback zugreifen. Anscheinend bezieht sich this nicht auf das erstellte Objekt, 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?

1093
Felix Kling

Was Sie über this wissen sollten

this (auch "der Kontext") ist ein spezielles Schlüsselwort in jeder Funktion. Der Wert hängt nur von wie die Funktion abgerufen wird, nicht davon ab, wie und wann/wo sie definiert wurde. Sie wird nicht wie andere Variablen von lexikalischen Bereichen beeinflusst (außer 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 Informationen zu this finden Sie in der MDN-Dokumentation .


Wie verweise ich auf die richtige this

Verwenden Sie nicht this

Sie möchten eigentlich nicht speziell auf this zugreifen, sondern das Objekt, auf das verweist. Deshalb ist es eine einfache Lösung, einfach eine neue Variable zu erstellen, die sich auch auf dieses Objekt bezieht. Die Variable kann einen beliebigen Namen haben, die häufigsten sind jedoch self und that.

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

Da self eine normale Variable ist, gehorcht es lexikalischen Bereichsregeln und ist innerhalb des Rückrufs zugänglich. Dies hat auch den Vorteil, dass Sie auf den this-Wert des Callbacks selbst zugreifen können.

this des Callback - Teil 1 explizit setzen

Es könnte so aussehen, als hätten Sie keine Kontrolle über den Wert von this, da der Wert automatisch festgelegt wird. Dies ist jedoch nicht der Fall. 

Jede Funktion hat die Methode .bind[docs] , gibt eine neue Funktion zurück, bei der this an einen Wert gebunden ist. Die Funktion hat genau dasselbe Verhalten wie die, die Sie .bind angerufen haben, nur die this wurde von Ihnen festgelegt. Unabhängig davon, wie oder wann diese Funktion aufgerufen wird, verweist this 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 this des Rückrufs an den Wert von MyConstructor der this.

Hinweis: Verwenden Sie beim Bindungskontext für jQuery jQuery.proxy[docs] stattdessen. Der Grund dafür ist, dass Sie beim Lösen eines Ereignisrückrufs den Verweis auf die Funktion nicht speichern müssen. jQuery behandelt das intern. 

ECMAScript 6: Verwenden Sie Pfeilfunktionen

ECMAScript 6 führt arrow-Funktionen ein, die man sich als Lambda-Funktionen vorstellen kann. Sie haben keine eigene this-Bindung. Stattdessen wird this wie eine normale Variable im Gültigkeitsbereich nachgeschlagen. Das heißt, Sie müssen nicht .bind aufrufen. 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));
}

Stellen Sie this für den Rückruf ein - Teil 2

Einige Funktionen/Methoden, die Callbacks akzeptieren, akzeptieren auch einen Wert, auf den sich die this des Callbacks beziehen soll. Dies ist im Grunde dasselbe wie das Binden selbst, aber die Funktion/Methode erledigt das für Sie. Array#map[docs] ist eine solche Methode. Ihre Unterschrift lautet:

array.map(callback[, thisArg])

Das erste Argument ist der Rückruf und das zweite Argument ist der Wert, auf den sich this beziehen 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 this übergeben können, wird in der Dokumentation der jeweiligen Funktion/Methode normalerweise erwähnt. Zum Beispiel die $.ajax-Methode von jQuery [docs] beschreibt eine Option mit dem Namen context:

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


Häufiges Problem: Verwendung von Objektmethoden als Callbacks/Event-Handler

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 einen Wert einer Objekteigenschaft darstellt. Diese Funktion hat jedoch keinen bestimmten 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.method wird als Click-Ereignishandler zugewiesen. Wenn Sie jedoch auf document.body klicken, wird der protokollierte Wert undefined sein, da this im Eventhandler auf document.body und nicht auf die Instanz von Foo verweist.
Wie bereits erwähnt, hängt die Bezeichnung von this davon ab, wie die Funktion aufgerufen wird, und nicht wie sie definiert ist.
Wenn der Code dem folgenden ähnelt, 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 die gleiche wie oben erwähnt: Falls verfügbar, verwenden Sie .bind, um this explizit an einen bestimmten Wert zu binden

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

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

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

oder verwende eine Pfeilfunktion:

document.body.onclick = () => this.method();
1458
Felix Kling

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

  1. Sie können die Funktion bind() verwenden. 
  2. Speichern Sie den Verweis auf context/this in einer anderen Variablen (siehe Beispiel unten).
  3. Verwenden Sie die Funktionen von ES6 Arrow .
  4. Code/Funktionsdesign/Architektur ändern - dafür sollten Sie über Design Patterns in Javascript verfügen.

1. Verwenden Sie die Funktion bind()

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 underscore.js - http://underscorejs.org/#bind verwenden 

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

2 Speichern Sie den Verweis auf den Kontext 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);
  });
}
163
Mohan Dere

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

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 dieselbe Methode aufrufen, jedoch in separaten Schritten, ist der Kontext der globale Gültigkeitsbereich (Fenster):

var f = object.property;
f();

Wenn Sie die Referenz einer Methode erhalten, ist sie nicht mehr an das Objekt angehängt, sondern lediglich eine Referenz auf eine plain-Funktion. Dasselbe passiert, wenn Sie die Referenz erhalten, die als Rückruf verwendet werden soll:

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 stattdessen die $.proxy-Methode verwenden, da bind nicht in allen Browsern unterstützt wird:

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

Das Problem mit "Kontext"

Der Begriff "Kontext" wird manchmal verwendet, um auf das Objekt zu verweisen, auf das mit this verwiesen wird. Seine Verwendung ist ungeeignet, da es weder semantisch noch technisch zu ECMAScript's this passt.

"Kontext" bezeichnet die Umstände, die etwas umgeben, das Bedeutung hinzufügt, oder einige vorangegangene und folgende Informationen, die zusätzliche Bedeutung haben. Der Begriff "Kontext" wird in ECMAScript verwendet, um auf Ausführungskontext zu verweisen. Hierbei handelt es sich um alle Parameter, Gültigkeitsbereich und this im Geltungsbereich einiger ausführender Codes.

Dies ist in ECMA-262, Abschnitt 10.4.2 gezeigt:

Setzen Sie ThisBinding auf den gleichen Wert wie ThisBinding von Aufruf des Ausführungskontextes

was eindeutig anzeigt, dass this Teil eines Ausführungskontextes ist.

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

Der Wert von this 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 strikten Modus auf einen beliebigen Wert gesetzt werden kann. 

22
RobG

Zunächst müssen Sie ein klares Verständnis von scope und dem Verhalten von this im Zusammenhang mit scope haben.

this & scope:


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

   1) Global Scope

   2) Function Scope

kurz gesagt, bezieht sich der globale Gültigkeitsbereich auf das Fensterobjekt.Variablen, die in einem globalen Gültigkeitsbereich deklariert sind, sind von überall her zugänglich. Andererseits befindet sich der Funktionsumfang innerhalb einer Funktion. Innerhalb einer Funktion deklarierte Variablen sind normalerweise nicht von außerhalb der Welt zugänglich .this Das Schlüsselwort im globalen Gültigkeitsbereich bezieht sich auf das Fensterobjekt .this inside-Funktion bezieht sich auch auf das Fensterobjekt.So this verweist immer auf das Fenster, bis wir eine Möglichkeit finden, this zu bearbeiten. einen Kontext unserer Wahl angeben.

--------------------------------------------------------------------------------
-                                                                              -
-   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, this in Callback-Funktionen zu bearbeiten:

Hier habe ich eine Konstruktorfunktion namens Person. Es hat eine Eigenschaft namens name und vier Methoden namens 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 die Namenseigenschaft einer Instanz der Person-Konstruktorfunktion protokolliert.

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 nun eine Instanz aus dem Personenkonstruktor erstellen und verschiedene Versionen der sayNameVersionX (X bezieht sich auf die 1,2,3,4) -Methode mit niceCallback aufrufen, um zu sehen, auf wie viele Arten wir die this inside callback, um auf die person -Instanz zu verweisen.

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

binden :

Was bind tut, ist das Erstellen einer neuen Funktion, wobei das Schlüsselwort this auf den angegebenen Wert gesetzt ist.

sayNameVersion1 und sayNameVersion2 verwenden Bind, um this der Rückruffunktion zu manipulieren.

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

erstes bind this mit Callback in der Methode selbst. Und für das zweite Callback wird das Objekt 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 :

Der first argument der call -Methode wird als this innerhalb der Funktion verwendet, die mit call aufgerufen wird.

sayNameVersion3 verwendet call, um die this zu manipulieren, um auf das von uns erstellte Personenobjekt zu verweisen, anstatt auf das Fensterobjekt.

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

und es heißt wie folgt:

p1.sayNameVersion3(niceCallback)

sich bewerben :

Ähnlich wie call bezieht sich das erste Argument von apply auf das Objekt, das durch das Schlüsselwort this angegeben wird.

sayNameVersion4 verwendet apply, um this zu manipulieren, um auf Personenobjekte zu verweisen

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

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

p1.sayNameVersion4(niceCallback)
18
AL-zami

Wir können dies nicht an setTimeout() binden, da es immer mit global object (Window) ausgeführt wird. Wenn Sie auf this-Kontext in der Rückruffunktion zugreifen möchten, können Sie mit bind() für die Rückruffunktion Folgendes erreichen:

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

Sie sollten "dieses" Keyword kennen. 

Meiner Ansicht nach können Sie "this" auf drei Arten implementieren(Self/Arrow-Funktion/Bind-Methode)

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

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

In den meisten Fällen wird der Wert davon durch den Aufruf einer Funktion bestimmt.

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

ES5 führte die bind () - Methode ein, um den Wert einer Funktion unabhängig von ihrem Aufruf festzulegen.

und ES2015 haben Pfeilfunktionen eingeführt, die diese Bindung nicht bereitstellen (sie behält den Wert des umgebenden lexikalischen Kontextes bei).

Method1: Self-Self wird verwendet, um einen Verweis auf das Original beizubehalten, auch wenn sich der Kontext ändert. Dies ist eine Technik, die häufig in Event-Handlern verwendet wird (insbesondere bei Schließungen).

Reference: 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);
    });
}

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

allerdings ohne eigene Bindungen an die Schlüsselwörter this, arguments, super oder new.target.

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

Reference: 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);
    });
}

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

bei Aufruf wird dieses Schlüsselwort auf den angegebenen Wert gesetzt.

mit einer gegebenen Folge von Argumenten, die vor jedem angegeben werden, wenn die neue Funktion aufgerufen wird.

Reference: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);
8
ashish

Ein weiterer Ansatz, der die Standardmethode seit DOM2 ist, um this innerhalb des Ereignis-Listeners zu binden, dass Sie den Listener (neben anderen Vorteilen) immer entfernen können, ist die handleEvent(evt)method aus der EventListener-Schnittstelle:

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

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

Ausführliche Informationen zur Verwendung von handleEvent finden Sie hier: https://medium.com/@WebReflection/dom-handleevent-a-cross-platform-standard-since-year-2000-5bf17287fd38

2
Andrea Puddu

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

Mit Unterstützung von class Feldern ist es möglich, den nächsten Weg zu machen:

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

Unter der Haube ist zwar alles gute Pfeilfunktion, die den Kontext bindet, aber in dieser Form erscheint diese explizite Bindung viel klarer.

Da es sich um einen Vorschlag für Stufe 3 handelt, benötigen Sie babel und das entsprechende babel-Plugin , um es vorerst (08/2018) zu verarbeiten.

2
skyboyer

Die Frage dreht sich um das Verhalten von this in Javascript. this verhält sich anders als unten,

  1. Der Wert davon wird normalerweise von einem Funktionsausführungskontext bestimmt.
  2. Im globalen Bereich bezieht sich dies auf das globale Objekt (das Fensterobjekt).
  3. Wenn der strikte Modus für eine Funktion aktiviert ist, ist der Wert von "this" "undefiniert", da das globale Objekt anstelle des Windows-Objekts auf undefined verweist.
  4. Das Objekt, das vor dem Punkt steht, ist das, an das dieses Schlüsselwort gebunden wird.
  5. Wir können den Wert explizit mit call (), bind () und apply () festlegen.
  6. Wenn das neue Schlüsselwort verwendet wird (ein Konstruktor), ist dieses an das neue Objekt gebunden, das erstellt wird.
  7. Pfeilfunktionen binden das nicht - stattdessen ist dies lexikalisch gebunden (d. H. Basierend auf dem ursprünglichen Kontext).

Wie die meisten Antworten nahe legen, können wir die Arrow-Funktion oder bind() Method oder Self var verwenden. Ich würde einen Punkt zu Lambdas (Pfeilfunktion) aus Google JavaScript Style Guide zitieren.

Verwenden Sie lieber Pfeilfunktionen über f.bind (this) und vor allem über goog.bind (f, this). Schreiben Sie const self = this nicht. Pfeilfunktionen sind besonders nützlich für Rückrufe, die manchmal unerwartet passieren zusätzliche Argumente.

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

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-vs-bind
0
Code_Mode