Ich bin gerade auf eine interessante Situation in JavaScript gestoßen. Ich habe eine Klasse mit einer Methode, die mehrere Objekte in Objekt-Literal-Notation definiert. In diesen Objekten wird der this
Zeiger verwendet. Aus dem Verhalten des Programms habe ich abgeleitet, dass sich der this
Zeiger auf die Klasse bezieht, für die die Methode aufgerufen wurde, und nicht auf das Objekt, das vom Literal erstellt wird.
Dies scheint willkürlich, obwohl ich erwarten würde, dass es so funktioniert. Ist das definiertes Verhalten? Ist es browserübergreifend sicher? Gibt es eine Begründung dafür, warum es so ist, wie es jenseits der "Spezifikation sagt" (zum Beispiel ist es eine Folge einer umfassenderen Designentscheidung / -philosophie)? Beispiel für einen reduzierten Code:
// inside class definition, itself an object literal, we have this function:
onRender: function() {
this.menuItems = this.menuItems.concat([
{
text: 'Group by Module',
rptletdiv: this
},
{
text: 'Group by Status',
rptletdiv: this
}]);
// etc
}
var signup = { onLoadHandler:function(){ console.log(this); return Type.createDelegate(this,this._onLoad); }, _onLoad: function (s, a) { console.log("this",this); }};
Antworten:
Schlachtete aus einem anderen Beitrag von mir, hier ist mehr , als Sie jemals darüber wissen wollten dies .
Bevor ich anfange, ist hier das Wichtigste, was Sie über Javascript wissen und sich wiederholen sollten, wenn es keinen Sinn ergibt. Javascript hat keine Klassen (ES6
class
ist syntaktischer Zucker ). Wenn etwas wie eine Klasse aussieht, ist es ein kluger Trick. Javascript hat Objekte und Funktionen . (Das ist nicht 100% genau, Funktionen sind nur Objekte, aber es kann manchmal hilfreich sein, sie als separate Dinge zu betrachten.)Die Variable this ist an Funktionen angehängt. Jedes Mal , wenn Sie eine Funktion aufrufen, dies wird einen bestimmten Wert gegeben, je nachdem , wie Sie rufen die Funktion. Dies wird oft als Aufrufmuster bezeichnet.
Es gibt vier Möglichkeiten, Funktionen in Javascript aufzurufen. Sie können die Funktion als Methode , als Funktion , als Konstruktor und mit apply aufrufen .
Als Methode
Eine Methode ist eine Funktion, die an ein Objekt angehängt ist
Wenn sie als eine Methode aufgerufen, dies wird die Funktion / Methode an das Objekt gebunden wird , ist ein Teil der. In diesem Beispiel ist dies an foo gebunden.
Als eine Funktion
Wenn Sie eine eigenständige Funktion haben, wird diese Variable an das "globale" Objekt gebunden, fast immer an das Fensterobjekt im Kontext eines Browsers.
Das mag dich stolpern lassen , aber fühle dich nicht schlecht. Viele Leute halten dies für eine schlechte Designentscheidung. Da ein Rückruf als Funktion und nicht als Methode aufgerufen wird, sehen Sie ein scheinbar inkonsistentes Verhalten.
Viele Leute umgehen das Problem, indem sie so etwas tun
Sie definieren eine Variable , dass der zu den Punkten dieses . Closure (ein Thema , all seine eigene) hält diese um, wenn Sie also bar als Rückruf nennen, es hat immer noch einen Verweis.
HINWEIS:
use strict
Wenn der Modus als Funktion verwendet wird,this
ist er nicht an global gebunden. (Es istundefined
).Als Konstrukteur
Sie können eine Funktion auch als Konstruktor aufrufen. Basierend auf der von Ihnen verwendeten Namenskonvention (TestObject) kann dies auch das sein, was Sie tun und was Sie auslöst .
Sie rufen eine Funktion als Konstruktor mit dem neuen Schlüsselwort auf.
Wenn als Konstruktor aufgerufen wird , wird ein neues Objekt erstellt werden, und dies wird auf das Objekt gebunden werden. Wenn Sie innere Funktionen haben und diese als Rückrufe verwendet werden, rufen Sie sie als Funktionen auf, und dies wird an das globale Objekt gebunden. Verwenden Sie diese var that = diesen Trick / dieses Muster.
Einige Leute denken, dass der Konstruktor / das neue Schlüsselwort ein Knochen war, der Java / traditionellen OOP-Programmierern als eine Möglichkeit zum Erstellen von Klassen ähnlichem geworfen wurde.
Mit der Apply-Methode
Schließlich hat jede Funktion eine Methode (ja, Funktionen sind Objekte in Javascript) mit dem Namen "apply". Bewerben können Sie bestimmen , was der Wert dieser sein wird, und lässt Sie auch in einer Reihe von Argumenten übergeben. Hier ist ein nutzloses Beispiel.
quelle
this
wirdundefined
für Funktionsaufrufe.this
hat nichts mit Umfang zu tun. Du meinst das globale Objekt .this.confusing = 'hell yeah';
das gleiche wievar confusing = 'hell yeah';
? Also werden beide erlaubenmyObject.confusing
? Es wäre schön, wenn nicht nur, damit Siethis
die Eigenschaften und andere Variablen für die interne Arbeit erstellen können .function Foo(thought){ this.confusing = thought; }
var myObject = new Foo("hell yeah");
Funktionsaufrufe
Funktionen sind nur eine Art Objekt.
Alle Funktionsobjekte verfügen über Methoden zum Aufrufen und Anwenden , mit denen das aufgerufene Funktionsobjekt ausgeführt wird.
Beim Aufruf gibt das erste Argument für diese Methoden das Objekt an, auf das das
this
Schlüsselwort während der Ausführung der Funktion verweist - wenn esnull
oderundefined
das globale Objektwindow
für verwendet wirdthis
.Aufrufen einer Funktion ...
... mit Klammern -
foo()
- entsprichtfoo.call(undefined)
oderfoo.apply(undefined)
, was praktisch dasselbe ist wiefoo.call(window)
oderfoo.apply(window)
.Zusätzliche Argumente,
call
die als Argumente an den Funktionsaufruf übergeben werden sollen, während ein einzelnes zusätzliches Argumentapply
die Argumente für den Funktionsaufruf als Array-ähnliches Objekt angeben kann.Somit
foo(1, 2, 3)
ist äquivalent zufoo.call(null, 1, 2, 3)
oderfoo.apply(null, [1, 2, 3])
.Wenn eine Funktion eine Eigenschaft eines Objekts ist ...
... der Zugriff auf einen Verweis auf die Funktion über das Objekt und der Aufruf mit Klammern -
obj.foo()
- entsprichtfoo.call(obj)
oderfoo.apply(obj)
.Funktionen, die als Eigenschaften von Objekten gehalten werden, sind jedoch nicht an diese Objekte "gebunden". Wie Sie in der
obj
obigen Definition sehen können , können Funktionen, da sie nur eine Art von Objekt sind, referenziert werden (und somit als Referenz auf einen Funktionsaufruf übergeben oder als Referenz von einem Funktionsaufruf zurückgegeben werden). Wenn ein Verweis auf eine Funktion übergeben wird, werden keine zusätzlichen Informationen darüber mitgeführt, woher er übergeben wurde. Aus diesem Grund geschieht Folgendes:Der Aufruf unserer Funktionsreferenz
baz
bietet keinen Kontext für den Aufruf, ist also praktisch derselbe wiebaz.call(undefined)
, führt also zuthis
einer Referenzierungwindow
. Wenn wirbaz
wissen wollen , dass es dazu gehört,obj
müssen wir diese Informationen irgendwie bereitstellen, wenn siebaz
aufgerufen werden. Hier kommt das erste Argument fürcall
oderapply
und Schließungen ins Spiel.Zielfernrohrketten
Wenn eine Funktion ausgeführt wird, erstellt sie einen neuen Bereich und verweist auf einen beliebigen umschließenden Bereich. Wenn die anonyme Funktion im obigen Beispiel erstellt wird, verweist sie auf den Bereich, in dem sie erstellt wurde. Dies ist
bind
der Bereich. Dies ist als "Verschluss" bekannt.Wenn Sie versuchen, auf eine Variable zuzugreifen, wird diese "Bereichskette" durchlaufen, um eine Variable mit dem angegebenen Namen zu finden. Wenn der aktuelle Bereich die Variable nicht enthält, sehen Sie sich den nächsten Bereich in der Kette an und so weiter, bis Sie ihn erreichen der globale Umfang. Wenn die anonyme Funktion zurückgegeben wird und die
bind
Ausführung abgeschlossen ist, hat die anonyme Funktion immer noch einen Verweis aufbind
den Gültigkeitsbereich, sodassbind
der Gültigkeitsbereich nicht "verschwindet".Angesichts all der oben genannten Punkte sollten Sie nun in der Lage sein zu verstehen, wie der Gültigkeitsbereich im folgenden Beispiel funktioniert und warum die Technik zum Übergeben einer Funktion um "vorgebunden" mit einem bestimmten Wert
this
funktioniert, wenn sie aufgerufen wird:quelle
Ja. Und ja.
Die Bedeutung von
this
ist ziemlich einfach abzuleiten:this
es in einer Konstruktorfunktion verwendet wird und die Funktion mit demnew
Schlüsselwort aufgerufen wurde ,this
bezieht es sich auf das Objekt, das erstellt wird.this
wird weiterhin das Objekt auch in öffentlichen Methoden bedeuten.this
es an einer anderen Stelle verwendet wird, einschließlich verschachtelter geschützter Funktionen, bezieht es sich auf den globalen Bereich (der im Fall des Browsers das Fensterobjekt ist).Der zweite Fall ist offensichtlich ein Konstruktionsfehler, aber es ist ziemlich einfach, ihn mithilfe von Verschlüssen zu umgehen.
quelle
In diesem Fall ist das Innere
this
an das globale Objekt gebunden und nicht an diethis
Variable der äußeren Funktion. So ist die Sprache gestaltet.Eine gute Erklärung finden Sie unter "JavaScript: The Good Parts" von Douglas Crockford.
quelle
Ich habe ein nettes Tutorial zum ECMAScript gefunden
Jedes Objekt kann als dieser Wert des Kontexts verwendet werden.
Diese Funktion ist sehr wichtig, da dieser Wert im Gegensatz zu Variablen niemals am Prozess der Auflösung von Bezeichnern teilnimmt. Das heißt, wenn in einem Code darauf zugegriffen wird, wird sein Wert direkt aus dem Ausführungskontext und ohne Suche nach Bereichsketten entnommen. Der Wert davon wird nur einmal beim Betreten des Kontexts bestimmt.
Im globalen Kontext ist ein dieser Wert das globale Objekt selbst (dh dieser Wert entspricht hier einem variablen Objekt).
Im Falle eines Funktionskontexts kann dieser Wert in jedem einzelnen Funktionsaufruf unterschiedlich sein
Referenz Javascript-the-Core und Kapitel-3-this
quelle