it-swarm.com.de

Wie funktioniert "dieses" Schlüsselwort in einer Funktion?

Ich bin gerade auf eine interessante Situation in JavaScript gestoßen. Ich habe eine Klasse mit einer Methode, die mehrere Objekte mit Objekt-Literal-Notation definiert. In diesen Objekten wird der Zeiger this verwendet. Aus dem Verhalten des Programms habe ich abgeleitet, dass der Zeiger this auf die Klasse verweist, für die die Methode aufgerufen wurde, und nicht auf das Objekt, das vom Literal erstellt wird.

Dies scheint willkürlich, obwohl ich davon ausgehen würde, dass es funktioniert. Ist das definiertes Verhalten? Ist es browserübergreifend sicher? Gibt es Gründe, warum es so ist, wie es jenseits der "Spezifikation sagt" (ist es zum Beispiel eine Folge einer allgemeineren Designentscheidung/-philosophie)? Reduziertes Codebeispiel:

// 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
}
246
rmeador

Kannibalisiert von einem anderen Posten von mir, hier ist mehr als Sie jemals über das wissen wollten.

Bevor ich anfange, ist hier das Wichtigste, was Sie bei Javascript beachten und sich selbst 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 wird an Funktionen angehängt. Wann immer Sie eine Funktion aufrufen, wird this ein bestimmter Wert zugewiesen, abhängig davon, wie Sie die Funktion aufrufen. 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 bewerben.

Als Methode

Eine Methode ist eine Funktion, die an ein Objekt angehängt ist

var foo = {};
foo.someMethod = function(){
    alert(this);
}

Beim Aufruf als Methode wird this an das Objekt gebunden, zu dem die Funktion/Methode gehört. In diesem Beispiel ist dies an foo gebunden.

Als eine Funktion

Wenn Sie eine eigenständige Funktion haben, wird die Variable this an das "globale" Objekt gebunden, fast immer an das Objekt window im Kontext eines Browsers .

 var foo = function(){
    alert(this);
 }
 foo();

Dies mag dich auslösen , 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 Menschen umgehen das Problem, indem sie so etwas tun

var foo = {};
foo.someMethod = function (){
    var that=this;
    function bar(){
        alert(that);
    }
}

Sie definieren eine Variable das, die auf das verweist. Der Abschluss (ein Thema, das alles ist, was ihm eigen ist) behält das bei. Wenn Sie also bar als Rückruf aufrufen, hat es immer noch eine Referenz.

HINWEIS: In use strict mode Bei Verwendung als Funktion ist this nicht an global gebunden. (Es ist undefined).

Als Konstrukteur

Sie können auch eine Funktion 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.

function Foo(){
    this.confusing = 'hell yeah';
}
var myObject = new Foo();

Beim Aufruf als Konstruktor wird ein neues Objekt erstellt und this an dieses Objekt gebunden. Wenn Sie innere Funktionen haben und diese als Rückrufe verwendet werden, rufen Sie sie als Funktionen auf, und this wird an das globale Objekt gebunden. Benutze das var that = this trick/pattern.

Einige Leute denken, das Schlüsselwort constructor/new sei ein Bone gewesen, der Java/traditional OOP) -Programmierern als Möglichkeit zur Erstellung von etwas ähnlichem wie Klassen vorgeworfen wurde.

Mit der Apply-Methode

Schließlich hat jede Funktion eine Methode (ja, Funktionen sind Objekte in Javascript) mit dem Namen "apply". Mit Apply können Sie den Wert von this bestimmen und eine Reihe von Argumenten übergeben. Hier ist ein unbrauchbares Beispiel.

function foo(a,b){
    alert(a);
    alert(b);
    alert(this);
}
var args = ['ah','be'];
foo.apply('omg',args);
557
Alan Storm

Funktionsaufrufe

Funktionen sind nur eine Art von Objekt.

Alle Funktionsobjekte haben Aufruf und Anwenden Methoden, die das Funktionsobjekt ausführen, auf das sie aufgerufen werden.

Beim Aufruf gibt das erste Argument dieser Methoden das Objekt an, auf das während der Ausführung der Funktion durch das Schlüsselwort this verwiesen wird - wenn es sich um null oder undefined handelt. Das globale Objekt window wird für this verwendet.

Also, eine Funktion aufrufen ...

whereAmI = "window";

function foo()
{
    return "this is " + this.whereAmI + " with " + arguments.length + " + arguments";
}

... mit Klammern - foo() - entspricht foo.call(undefined) oder foo.apply(undefined), was effektiv ist das gleiche wie foo.call(window) oder foo.apply(window).

>>> foo()
"this is window with 0 arguments"
>>> foo.call()
"this is window with 0 arguments"

Zusätzliche Argumente für call werden als Argumente an den Funktionsaufruf übergeben, während ein einzelnes zusätzliches Argument für apply die Argumente für den Funktionsaufruf als Array-ähnliches Objekt angeben kann.

Somit ist foo(1, 2, 3) äquivalent zu foo.call(null, 1, 2, 3) oder foo.apply(null, [1, 2, 3]).

>>> foo(1, 2, 3)
"this is window with 3 arguments"
>>> foo.apply(null, [1, 2, 3])
"this is window with 3 arguments"

Wenn eine Funktion eine Eigenschaft eines Objekts ist ...

var obj =
{
    whereAmI: "obj",
    foo: foo
};

... der Zugriff auf eine Referenz auf die Funktion über das Objekt und der Aufruf mit Klammern - obj.foo() - entspricht foo.call(obj) oder foo.apply(obj).

Funktionen, die als Eigenschaften von Objekten gehalten werden, sind jedoch nicht an diese Objekte "gebunden". Wie Sie in der obigen Definition von obj sehen können, können Funktionen, da sie nur ein Objekttyp sind, referenziert werden (und können daher 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 sie übergeben wurde . Aus diesem Grund geschieht Folgendes:

>>> baz = obj.foo;
>>> baz();
"this is window with 0 arguments"

Der Aufruf unserer Funktionsreferenz baz stellt keinen Kontext für den Aufruf bereit, daher ist er im Grunde genommen derselbe wie baz.call(undefined), sodass this auf den Aufruf verweist window. Wenn wir möchten, dass baz weiß, dass es zu obj gehört, müssen wir diese Informationen irgendwie bereitstellen, wenn baz aufgerufen wird. Hier ist das erste Argument für call oder apply und Verschlüsse kommen ins Spiel.

Scope-Ketten

function bind(func, context)
{
    return function()
    {
        func.apply(context, arguments);
    };
}

Wenn eine Funktion ausgeführt wird, erstellt sie einen neuen Bereich und verweist auf einen umschließenden Bereich. Wenn die anonyme Funktion im obigen Beispiel erstellt wird, verweist sie auf den Bereich, in dem sie erstellt wurde. Dies ist der Bereich von bind. Dies ist als "Schließung" bekannt.

[global scope (window)] - whereAmI, foo, obj, baz
    |
    [bind scope] - func, context
        |
        [anonymous scope]

Wenn Sie versuchen, auf eine Variable zuzugreifen, wird in dieser "Gültigkeitsbereichskette" nach einer Variablen mit dem angegebenen Namen gesucht. Wenn der aktuelle Gültigkeitsbereich die Variable nicht enthält, sehen Sie sich den nächsten Gültigkeitsbereich in der Kette an und so weiter, bis Sie zu gelangen der globale Geltungsbereich. Wenn die anonyme Funktion zurückgegeben wird und die Ausführung von bind beendet ist, enthält die anonyme Funktion immer noch einen Verweis auf den Bereich von bind, sodass der Bereich von bind nicht "verschwindet". .

In Anbetracht der obigen Ausführungen sollten Sie nun verstehen können, wie der Gültigkeitsbereich im folgenden Beispiel funktioniert und warum die Technik zum Übergeben einer Funktion um "vorgebunden" mit einem bestimmten Wert von this, wenn es vorhanden ist heißt Werke:

>>> baz = bind(obj.foo, obj);
>>> baz(1, 2);
"this is obj with 2 arguments"
35
Jonny Buchanan

Ist das definiertes Verhalten? Ist es browserübergreifend sicher?

Ja. Und ja.

Gibt es Gründe, warum es so ist, wie es ist ...

Die Bedeutung von this ist ziemlich einfach abzuleiten:

  1. Wenn this in einer Konstruktorfunktion verwendet wird und die Funktion mit dem Schlüsselwort new aufgerufen wurde, bezieht sich this auf das zu erstellende Objekt. this wird weiterhin das Objekt auch in öffentlichen Methoden bedeuten.
  2. Wenn this 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 mit Verschlüssen zu umgehen.

9
Rakesh Pai

In diesem Fall wird das innere this an das globale Objekt gebunden und nicht an die Variable this der äußeren Funktion. So ist die Sprache gestaltet.

Eine gute Erklärung finden Sie in "JavaScript: The Good Parts" von Douglas Crockford.

4
Santiago Cepas

Ich fand ein nettes Tutorial über das ECMAScript dieses

Dieser Wert ist ein spezielles Objekt, das mit dem Ausführungskontext verknüpft ist. Daher kann es als Kontextobjekt bezeichnet werden (d. H. Als Objekt, in dem der Ausführungskontext aktiviert ist).

Jedes Objekt kann als dieser Wert des Kontexts verwendet werden.

dieser Wert ist eine Eigenschaft des Ausführungskontexts, jedoch keine Eigenschaft des variablen Objekts.

Diese Funktion ist sehr wichtig, da dieser Wert im Gegensatz zu Variablen niemals an der Bezeichnerauflösung beteiligt ist. Das heißt Wenn Sie in einem Code darauf zugreifen, wird der Wert direkt aus dem Ausführungskontext und ohne Nachschlagen der Bereichskette übernommen. Der Wert davon wird nur einmal beim Eintreten in den Kontext festgelegt.

Im globalen Kontext ist ein this-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

4
Damodaran