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?
this
wissen solltenthis
(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 .
this
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 setzenEs 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 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));
}
this
für den Rückruf ein - Teil 2Einige 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.
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();
bind()
verwenden. 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));
function MyConstructor(data, transport) {
var self = this;
this.data = data;
transport.on('data', function() {
alert(self.data);
});
}
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', () => {
alert(this.data);
});
}
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));
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.
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
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
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)
Ä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)
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);
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);
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
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.
Die Frage dreht sich um das Verhalten von this
in Javascript. this
verhält sich anders als unten,
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: