Verschlüsse in Javascript [Teil 1]

Übersetzt von Richard Kornford Javascript Closures .

  • Einleitung
  • Objekt Eigenschaftsname Auflösung
    • Wertzuweisung
    • Werte lesen

  • Bezeichnernamenauflösung, Ausführungskontexte und Bereichskette
    • Ausführungskontext
    • Die Scope-Kette und die Eigenschaft [[scope]]
    • Auflösung des Bezeichnernamens

  • ...

Einleitung


Kurzschluss
Ein Abschluß ist ein Ausdruck (normalerweise eine Funktion), der freie Variablen enthalten kann, zusammen mit der Umgebung, die diese Variablen bindet (dh diesen Ausdruck abschließt).

Verschlüsse gehören zu den leistungsfähigsten Funktionen von ECMAScript (Javascript), können jedoch ohne Verständnis nicht richtig angewendet werden. Trotz der Tatsache, dass sie auch aus Versehen leicht zu erstellen sind, kann ihre Erstellung nachteilige Folgen haben, insbesondere in einigen relativ gebräuchlichen Browserumgebungen. Um versehentliche Kollisionen mit Fehlern zu vermeiden und Fehler auszunutzen, ist es notwendig, deren Mechanismus zu verstehen. Dies hängt in hohem Maße von der Rolle der Bereichskette bei der Bezeichnerauflösung und der Auflösung von Eigenschaftsnamen in Objekten ab.

Die einfachste Erklärung für Closures ist, dass ECMAScript verschachtelte Funktionen, Funktionsdefinitionen und Funktionsausdrücke in den Körpern anderer Funktionen zulässt. Und diese verschachtelten Funktionen haben Zugriff auf alle lokalen Variablen, Parameter und Funktionen innerhalb ihrer externen Funktion (externe Funktionen). Ein Abschluss wird gebildet, wenn eine dieser verschachtelten Funktionen außerhalb der Funktion verfügbar wird, in der sie enthalten war, sodass sie nach Abschluss der externen Funktion ausgeführt werden kann. Zu diesem Zeitpunkt hat sie noch Zugriff auf die lokalen Variablen, Parameter und internen Funktionsdeklarationen ihrer externen Funktion. Diese lokalen Variablen, Parameter und Funktionsdeklarationen haben (anfangs) die gleiche Bedeutung.

Leider erfordert ein korrektes Verständnis der Verschlüsse ein Verständnis der dahinter stehenden Mechanismen und viele technische Details. Obwohl einige der in ECMA 262 definierten Algorithmen zu Beginn der folgenden Erläuterung erwähnt werden, können die meisten nicht weggelassen oder einfach auf eine vereinfachte Form reduziert werden. Wenn Sie mit dem Auflösen von Objekteigenschaftsnamen vertraut sind, können Sie diesen Abschnitt überspringen, aber nur Personen, die bereits mit Abschlüssen vertraut sind, können es sich leisten, nachfolgende Abschnitte zu überspringen und das Lesen sofort zu beenden und sie wieder zu verwenden.

Objekt Eigenschaftsname Auflösung


ECMAScript erkennt zwei Kategorien von Objekten: native Objekte und Host-Objekte sowie eine Unterkategorie von nativen Objekten, die als integrierte Objekte bezeichnet werden (ECMA 262, 3. Aufl., Abschnitt 4.3). Native Objekte gehören zur Sprache, und Hostobjekte werden von der Umgebung bereitgestellt und können beispielsweise ein Dokumentobjekt, DOM-Knoten usw. sein.

Native Objekte sind freie und dynamische Container mit benannten Eigenschaften (einige Implementierungen sind in Bezug auf eine Unterkategorie eingebetteter Objekte nicht so dynamisch, obwohl dies normalerweise keine Rolle spielt). Bestimmte benannte Eigenschaften eines nativen Objektspeichers können auf ein anderes Objekt verweisen (Funktionen sind auch Objekte in diesem Sinne) oder elementare Werte: String, Number, Boolean, Null oder Undefined. Der Grundtyp Undefiniert ist etwas ungewöhnlich in dem Sinne, dass Sie die Eigenschaft eines Objekts auf undefiniert setzen können, die Eigenschaft jedoch nicht aus dem Objekt gelöscht wird. Es bleibt die benannte Eigenschaft des Objekts, in der der Wert einfach undefiniert gespeichert wird .

Im Folgenden wird vereinfacht erläutert, wie die Eigenschaften von Objekten gelesen und festgelegt werden, wobei die internen Details so weit wie möglich beeinflusst werden.

Wertzuweisung


Benannte Eigenschaften von Objekten können erstellt oder Werte vorhandener benannter Eigenschaften festgelegt werden, indem einer benannten Eigenschaft ein Wert zugewiesen wird.
Auf diese Weise,
var objectRef = new Object(); // создает обобщенный (generic) объект javascript.

Eine Eigenschaft mit dem Namen "testNumber" kann folgendermaßen erstellt werden:
objectRef.testNumber = 5;
/*  или  */
objectRef["testNumber"] = 5;

Das Objekt hatte vor dem Zuweisen des Werts nicht die Eigenschaft testNumber , sondern wurde erst danach erstellt. Bei nachfolgenden Zuweisungen muss diese Eigenschaft nicht erstellt werden. Sie ändert lediglich ihren Wert.
objectRef.testNumber = 8;
/* или  */
objectRef["testNumber"] = 8;

Javascript-Objekte haben Prototypen, die selbst Objekte sein können, wie nachfolgend kurz beschrieben wird, und diese Prototypen können benannte Eigenschaften haben. Dies gilt jedoch nicht für die Abtretung. Wenn ein Wert zugewiesen ist und das Objekt keine Eigenschaft mit dem entsprechenden Namen hat, wird diese Eigenschaft erstellt und ein Wert zugewiesen. Wenn das Objekt eine solche Eigenschaft hat, wird sein Wert zurückgesetzt.

Werte lesen


Beim Lesen der Werte werden Prototypen von Objekten verwendet. Wenn das Objekt eine Eigenschaft mit dem Namen hat, der im Eigenschaftszugriffsausdruck verwendet wird, wird der Wert dieser Eigenschaft zurückgegeben
/* Присвоение значение именованному свойству. Если у объекта не было свойства
   с соответствующим именем до присваивания, то оно появится после
*/
objectRef.testNumber = 8;
/* Считываем значение из свойства */
var val = objectRef.testNumber;
/* и val теперь содержит значение 8, которое было только 
что присвоено именованному свойству объекта. */ 

Aber alle Objekte können Prototypen haben, und Prototypen sind Objekte und können wiederum Prototypen haben, die Prototypen usw. haben und eine sogenannte Prototypenkette bilden. Die Prototypkette endet, wenn eines der Objekte in der Kette einen Null- Prototyp hat . Der Prototyp, mit dem Standard - Konstruktor Object hat einen Prototyp null , so,
var objectRef = new Object(); // создает обобщенный (generic) объект javascript

Erstellt ein Objekt mit einem Prototyp Object.prototype , der selbst einen Null- Prototyp hat . Dann enthält die Prototypkette des objectRef- Objekts nur ein Objekt: Object.prototype . Jedoch
/* Функция “конструктор” для создания объектов типа MyObject1.*/
function MyObject1(formalParameter){
    /*Возьмем свойство сконструированного объекта testNumber и
      присвоим ему значение, переданное конструктору, как его первый
      аргумент 
    */
    this.testNumber = formalParameter;
}
/* Функция “конструктор” для создания объектов типа MyObject2 */ 
function MyObject2(formalParameter){
   /*Возьмем свойство testString сконструированного объекта и 
     присвоим ему значение, переданное конструктору, как его первый 
     аргумент
    */
    this.testString = formalParameter;
}
/*Следующая операция заменит прототип, созданный по умолчанию, 
  ассоциированный со всеми экземплярами объекта MyObject2, 
  экземпляром объекта MyObject1, 
  отправив аргумент 8 в конструктор MyObject1, 
  тогда у его свойства testNumber будет установлено это значение
*/
MyObject2.prototype = new MyObject1( 8 );
/*Наконец, создадим экземпляр функции MyObject2 и присвоим переменной objectRef
  ссылку на этот объект, передав в конструктор строку как первый аргумент
*/
var objectRef = new MyObject2( "String_Value" );

Die Instanz von MyObject2, auf die die Variable objectRef verweist, hat eine Prototypkette. Das erste Objekt in dieser Kette ist eine Instanz von MyObject1 , die erstellt und der Prototypeigenschaft des MyObject2- Konstruktors zugewiesen wurde . Eine Instanz von MyObject1 verfügt über ein Prototypobjekt, das standardmäßig der Prototypeigenschaft von MyObject1 zugewiesen wurde. Dieser Prototyp ist der Standardprototyp des Objekts , d. H. das Objekt, auf das Object.prototype verweist . Object.prototype hat einen Null- PrototypDaher endet die Kette an diesem Punkt.

Wenn ein Eigenschaftszugriffsausdruck versucht, eine benannte Eigenschaft aus dem von objectRef referenzierten Objekt zu lesen , kann die gesamte Prototypkette am Prozess teilnehmen. Im einfachen Fall
var val = objectRef.testString;

- Eine Instanz von MyObject2 , auf die über objectRef zugegriffen werden kann , verfügt über eine Eigenschaft mit dem Namen testString . Daher wird der Wert dieser Eigenschaft auf String_Value festgelegt und der Variablen val zugewiesen .
Dennoch,
var val = objectRef.testNumber;

kann die Eigenschaft nicht aus der Instanz der MyObject2- Funktion lesen , da Es hat keine solche Eigenschaft, aber die Variable val ist auf 8 gesetzt und nicht undefiniert , da der Interpreter das Objekt, das sein Prototyp ist, aufgrund einer erfolglosen Suche nach der entsprechenden benannten Eigenschaft im Objekt selbst überprüft. Der Prototyp ist eine Instanz der Funktion MyObject1 , die mit der Eigenschaft testNumber mit dem Wert 8 erstellt wurde, der dieser Eigenschaft zugewiesen wurde, sodass der Ausdruck für den Zugriff auf die Eigenschaft als Wert 8 berechnet wird . Weder MyObject1 noch MyObject2 definieren eine MethodetoString , aber wenn der Eigenschaftszugriffsausdruck versucht, den Wert der toString- Eigenschaft aus objectRef zu lesen ,
var val = objectRef.toString;

dann wird der Variablen val eine Funktionsreferenz zugewiesen. Diese Funktion ist die toString- Eigenschaft des Object.prototype- Objekts und wird als Ergebnis des Prototypüberprüfungsprozesses zurückgegeben: Das objectRef- Objekt wird überprüft , nachdem festgestellt wurde, dass die toString- Eigenschaft fehlt , und der objectRef- Prototyp wird überprüft , wenn sich herausstellt, dass diese Eigenschaft nicht vorhanden ist Der Prototyp wird getestet. Der Prototyp ist Object.prototype mit einer toString- Methode, und der Link kehrt zu dieser Funktion zurück.

Abschließend:
var val = objectRef.madeUpProperty;

- gibt undefiniert zurück , da der Prozess, der die Prototypkette verarbeitet, keine Eigenschaften mit dem Namen madeUpProperty in einem der Objekte findet, sondern letztendlich den Prototyp des Objekts Object.prototype erreicht , d. h . null , und dann endet der Prozess mit der Rückgabe von undefined .

Das Lesen der benannten Eigenschaften gibt den ersten Wert aus dem Objekt oder seiner Prototypkette zurück. Wenn Sie einer benannten Eigenschaft eines Objekts einen Wert zuweisen, wird eine Eigenschaft im Objekt selbst erstellt, sofern die entsprechende Eigenschaft noch nicht vorhanden ist.

Dies bedeutet, dass wenn objectRef.testNumber = 3 ein Wert zugewiesen wurde , die Eigenschaft testNumberwird in der Instanz der MyObject2- Funktion erstellt, und nachfolgende Versuche, den Wert zu lesen, geben den im Objekt festgelegten Wert zurück. Jetzt muss die Prototypkette nicht mehr den Zugriffsausdruck für die Eigenschaft ausführen, aber die Instanz von MyObject1 mit dem der Eigenschaft testNumber zugewiesenen Wert 8 wurde nicht geändert. Wenn Sie einem objectRef- Objekt einen Wert zuweisen, wird die entsprechende Eigenschaft in der Prototypkette ausgeblendet .

Beachten Sie, dass ECMAScript die interne Eigenschaft [[prototype]] des internen Typs Object definiert. Auf diese Eigenschaft kann nicht direkt mit Skripten zugegriffen werden, es gibt jedoch eine Kette von Objekten, auf die die interne Eigenschaft [[prototype]] verweist , die zum Auflösen von Ausdrücken für Zugriffseigenschaften verwendet wird. Diese Kette ist ein Prototyp einer Objektkette. Die öffentliche Prototypeigenschaft dient zum Zuweisen von Werten, Definieren und Bearbeiten von Prototypen in Verbindung mit der internen Prototypeigenschaft . Details der Beziehung zwischen diesen beiden Eigenschaften von Honig sind in ECMA 262 (3. Auflage) beschrieben und werden in dieser Diskussion nicht berücksichtigt.

Bezeichnernamenauflösung, Ausführungskontexte und Bereichskette


Ausführungskontext


Ein Ausführungskontext ist ein abstraktes Konzept, das in der ECMAScript-Spezifikation (ECMA 262 3rd Edition) verwendet wird, um das für ECMAScript-Implementierungen erforderliche Verhalten zu bestimmen. Die Spezifikation sagt nichts darüber aus, wie der Ausführungskontext implementiert werden soll, aber Ausführungskontexte weisen assoziative Attribute auf, die auf in der Spezifikation definierte Strukturen verweisen. Daher können sie als Objekte mit Eigenschaften konzipiert (oder sogar implementiert) werden, auch wenn sie geschlossen sind.

Der gesamte Javascript-Code wird im Ausführungskontext ausgeführt . Globaler Code (eingebetteter Code in einer HTML-Seite oder in einer JS-Datei oder ausgeführt, nachdem die Seite geladen (geladen) wurde) wird im globalen Ausführungskontext ausgeführtund jeder Funktionsaufruf (möglicherweise als Konstruktor) hat einen entsprechenden Ausführungskontext. Code, der mit der Funktion eval ausgeführt wird, erhält auch einen bestimmten Ausführungskontext. Da eval jedoch von Javascript-Programmierern nicht häufig verwendet wird, wird hier nicht darauf eingegangen. Spezifizierte Details zu Ausführungskontexten finden Sie in Abschnitt 10.2 von ECMA 262 (3. Ausgabe).

Wenn eine JavaScript-Funktion aufgerufen wird, wird der Ausführungskontext hinzugefügt. Wenn eine andere Funktion (oder dieselbe Funktion rekursiv) aufgerufen wird, wird ein neuer Ausführungskontext erstellt und der Ausführungsprozess fügt diesen Kontext während des Funktionsaufrufs hinzu. Wenn die aufgerufene Funktion beendet wird, wird das Original zurückgegeben.Ausführungskontext . Somit bildet der ausgeführte Javascript-Code einen Stapel von Ausführungskontexten .

Wenn ein Ausführungskontext erstellt wird , passieren mehrere Dinge in einer bestimmten Reihenfolge. Zunächst wird im Kontext der Ausführung ein Aktivierungsobjekt erstellt. Das Aktivierungsobjekt ist ein weiterer Mechanismus aus der Spezifikation. Es kann als Objekt betrachtet werden, weil es als Ergebnis zugreifbare benannte Eigenschaften hat, aber es ist kein gewöhnliches Objekt, weil Es gibt keinen Prototyp (zumindest ist der Prototyp nicht definiert), und im Javascript-Code können keine Links zu ihm vorhanden sein.

Der nächste Schritt beim Erstellen eines Ausführungskontexts zum Aufrufen einer Funktion besteht darin, ein Argumentobjekt zu erstellenDies ist ein Array-ähnliches Objekt mit Elementen, die durch Ganzzahlen indiziert sind und mit den Argumenten übereinstimmen, die in dieser Reihenfolge an die Funktion gesendet wurden. Es hat auch die Eigenschaften Länge und Länge (die für unsere Diskussion nicht relevant sind, Details sind in der Spezifikation). Eine Eigenschaft des Aktivierungsobjekts wird mit den Namensargumenten erstellt , denen ein Verweis auf das Argumentobjekt zugewiesen ist .

Dann weist der Ausführungskontext den Bereich zu . Ein Bereich besteht aus einer Liste (oder Kette) von Objekten. Jedes Funktionsobjekt hat eine interne Eigenschaft [[scope]](auf die wir noch näher eingehen werden), die ebenfalls aus einer Liste (oder Kette) von Objekten besteht. Der dem Ausführungskontext des Funktionsaufrufs zugewiesene Bereich besteht aus einer Liste, auf die durch die Eigenschaft [[scope]] des entsprechenden Funktionsobjekts verwiesen wird, und einem Aktivierungsobjekt, das am Anfang der Kette (oder am Anfang dieser Liste) hinzugefügt wird.

Dann erfolgt der Prozess des Erzeugens von Variablen (Variableninstanziierung) unter Verwendung eines Objekts, das in ECMA 262 als Objekt von Variablen definiert ist (Variablenobjekt). Gleichzeitig wird das Aktivierungsobjekt als Objekt von Variablen verwendet (beachten Sie, dass dies dasselbe Objekt ist, dies ist wichtig). Benannte Eigenschaften des Variablenobjekts werden für jeden Formalparameter der Funktion erstellt. Wenn die Argumente beim Aufrufen der Funktion diesen Parametern entsprechen, werden die Argumente diesen Eigenschaften zugewiesen (ansonsten undefiniert)) Die Definition interner Funktionen wird verwendet, um Funktionsobjekte zu erstellen, die den Objekteigenschaften von Variablen mit Namen zugewiesen sind, die den Namen von Funktionen entsprechen, die in Funktionsdeklarationen verwendet werden. Der letzte Schritt beim Erstellen von Variablen besteht darin, benannte Eigenschaften des Variablenobjekts zu erstellen, die allen in der Funktion definierten lokalen Variablen entsprechen.

Die im Variablenobjekt erstellten Eigenschaften, die den deklarierten lokalen Variablen entsprechen, erhalten beim Erstellen der Variablen zunächst den Wert undefiniert , die lokalen Variablen werden erst initialisiert, wenn die entsprechende Zuweisungsoperation während der Ausführung des Funktionshauptteilcodes ausgeführt wird.

Tatsächlich ein Aktivierungsobjekt mit der Eigenschaft argumentsund Objektvariablen mit dem Namen Eigenschaften zu den lokalen Variablen von Funktionen entsprechen - es ist ein und dasselbe Objekt, das uns die Kennung zu prüfen erlaubt Argumente als lokale Variable Funktionen.

Und schließlich Wert des Schlüsselwort zugeordnet diesen . Wenn sich dieser Wert auf ein Objekt bezieht, beziehen sich die mit diesem Präfix versehenen Eigenschaftenzugriffsausdrücke auf die Eigenschaften dieses Objekts. Wenn dieser Wert (innerhalb der zugewiesenen Funktion) null , dann dies wird auf das globale Objekt beziehen.

Die Verarbeitung des globalen Ausführungskontexts unterscheidet sich geringfügig, da er keine Argumente enthält, muss kein Aktivierungsobjekt definiert werden, auf das verwiesen werden soll. Der globale Ausführungskontext benötigt einen Bereich und seine Kette enthält nur ein Objekt - ein globales Objekt. Der globale Ausführungskontext durchläuft die Erstellung von Variablen, seine internen Funktionen sind gewöhnliche Deklarationsfunktionen der obersten Ebene, die den größten Teil des Javascript-Codes ausmachen. Ein globales Objekt wird als Objekt von Variablen verwendet, weshalb global deklarierte Funktionen zu Eigenschaften eines globalen Objekts werden. Dasselbe gilt für global deklarierte Variablen.

Der globale Ausführungskontext verwendet auch einen Verweis auf das globale Objekt als Wert für die dies .

Die Scope-Kette und die Eigenschaft [[scope]]


Die Gültigkeitskette des Ausführungskontexts des Funktionsaufrufs wird zusammengestellt, indem ein Aktivierungsobjekt (variables Objekt) am Anfang der Gültigkeitskette in der Eigenschaft [[scope]] des Funktionsobjekts hinzugefügt wird. Daher ist es wichtig zu verstehen, wie die interne Eigenschaft [[scope]] definiert wird .

In ECMAScript sind Funktionen Objekte, die beim Erstellen von Variablen aus Funktionsdeklarationen, beim Ausführen einer Ausdrucksfunktion (Funktionsausdruck) oder beim Aufrufen des Funktionskonstruktors erstellt werden .

Vom Function- Konstruktor erstellte Funktionsobjekte haben immer die Eigenschaft [[scope]] , die sich auf die Scope-Kette beziehtwelches nur das globale Objekt enthält.

Funktionsobjekte, die durch eine Funktionsdeklaration oder einen Funktionsausdruck erstellt wurden, haben den Gültigkeitsbereich des Ausführungskontexts, in dem sie erstellt wurden, der ihrer internen Eigenschaft [[scope]] zugewiesen ist .

Im einfachsten Fall einer globalen Funktionsdeklaration, wie z
function exampleFunction(formalParameter){
    ...   // код тела функции
}

Das entsprechende Funktionsobjekt wird beim Erstellen von Variablen für den globalen Ausführungskontext erstellt. Der globale Ausführungskontext hat einen Bereich , der nur aus einem globalen Objekt besteht.

Somit wird diesem erstellten Funktionsobjekt, auf das durch die Eigenschaft des globalen Objekts exampleFunction verwiesen wird , die interne Eigenschaft [[scope]] zugewiesen , die sich auf die Bereichskette bezieht, die nur das globale Objekt enthält.

Eine ähnliche Bereichskette wird während der Ausführung einer Ausdrucksfunktion in einem globalen Kontext zugewiesen.
var exampleFuncRef = function(){
    ...   // код тела функции
}

mit der Ausnahme, dass in diesem Fall die benannte Eigenschaft des globalen Objekts während der Erstellung der Variablen des globalen Ausführungskontexts erstellt wird, das Funktionsobjekt jedoch nicht erstellt wird und der Verweis darauf nicht der benannten Eigenschaft des globalen Objekts zugewiesen wird, bis der Zuweisungsausdruck ausgeführt wird. Die Erstellung der Funktion erfolgt jedoch weiterhin im globalen Ausführungskontext. Daher enthält die Eigenschaft [[scope]] des erstellten Funktionsobjekts nur das globale Objekt in der zugewiesenen Gültigkeitsbereichskette.

Interne Deklarationen von Funktionen und Ausdrucksfunktionen führen zur Erstellung von Funktionsobjekten, die im Kontext der Funktionsausführung erstellt werden, sodass sie eine komplexere Umfangskette erhalten. Betrachten Sie den folgenden Code, der eine Funktion mit einer Deklaration einer internen Funktion definiert und dann eine externe Funktion ausführt
function exampleOuterFunction(formalParameter){
    function exampleInnerFuncitonDec(){
        ... // тело внутренней функции
    }
    ...  // остальная часть тела внешней функции
}
exampleOuterFunction( 5 );

Ein Funktionsobjekt, das der Deklaration einer externen Funktion entspricht, wird während der Erstellung von Variablen im globalen Ausführungskontext erstellt. Daher enthält die Eigenschaft [[scope]] eine Bereichskette , die aus einem globalen Objekt besteht.

Wenn der globale Code einen exampleOuterFunction- Aufruf ausführt , wird für diesen Funktionsaufruf zusammen mit dem Aktivierungsobjekt (Variablenobjekt) ein neuer Ausführungskontext erstellt. Der Gültigkeitsbereich des neuen Ausführungskontexts wird zu einer Kette, die aus einem neuen Aktivierungsobjekt besteht, gefolgt von der Kette, auf die die Eigenschaft [[scope]] verweistexterne Funktion (nur ein globales Objekt). Der Prozess der Erstellung von Variablen des neuen Ausführungskontexts führt zur Erstellung eines Funktionsobjekts, das der Deklaration der internen Funktion entspricht. Der Eigenschaft [[scope]] dieser internen Funktion wird der Wert des Bereichs des Ausführungskontexts zugewiesen, in dem sie erstellt wurde. Die Scope-Kette enthält ein Aktivierungsobjekt, gefolgt von einem globalen Objekt.

Bis zu diesem Punkt wird alles automatisch ausgeführt und durch die Struktur und Ausführung des Quellcodes gesteuert. Die Ausführungsbereichskette definiert die Eigenschaften [[scope]] für die erstellten Funktionsobjekte und diese Eigenschaften der Funktionsobjekte [[scope]].Bestimmen Sie den Umfang für ihren Ausführungskontext (zusammen mit dem entsprechenden Aktivierungsobjekt). ECMAScript unterstützt die with- Anweisung jedoch als Mittel zur Änderung des Gültigkeitsbereichs.

Die with- Anweisung wertet den Ausdruck aus und wenn es sich um ein Objekt handelt, wird es dem Gültigkeitsbereich des aktuellen Ausführungskontexts hinzugefügt (direkt vor dem Aktivierungsobjekt / den Aktivierungsvariablen). Dann wertet with einen anderen Ausdruck aus (der ein Block sein kann) und stellt dann die Gültigkeitskette des Ausführungskontexts in ihrer ursprünglichen Form wieder her. Mit

AussageDie Deklaration der Funktion kann nicht beeinflusst werden, da die Erstellung von Funktionsobjekten während der Erstellung von Variablen erfolgt. Ausdrucksfunktionen können jedoch innerhalb der with- Anweisung ausgeführt werden
/* создадим глобальную переменную y, которая ссылается на объект */
var y = {x:5}; // объектный литерал со свойством x
function exampleFuncWith(){
    var z;
    /* Поставим объект, на который ссылается переменная y,
        в начало цепи областей видимости
    */
    with(y){
        /* вычислим функцию-выражение, чтобы создать объект функции,
           и присвоим ссылку на этот объект функции локальной
           переменной z
        */
        z = function(){
            ... // тело внутренней функции-выражения;
        }
    }
    ... 
}
/* выполним функцию exampleFuncWith */
exampleFuncWith();

Wenn exampleFuncWith aufgerufen wird , besteht der Ausführungskontext aus einer Bereichskette, die aus dem Aktivierungsobjekt dieser Funktion und dem folgenden globalen Objekt besteht. Wenn Sie die with- Anweisung ausführen, wird das Objekt, auf das die globale Variable y verweist , für die Dauer dieser Ausdrucksfunktion am Anfang dieser Scope-Kette eingefügt. Der Eigenschaft des Funktionsobjekts [[scope]] , das durch die Ausführung der Ausdrucksfunktion erstellt wurde, wird ein Wert zugewiesen, der dem Umfang des Ausführungskontexts entspricht, in dem dieses Objekt erstellt wurde. Der Gültigkeitsbereich besteht aus dem Objekt ygefolgt vom Aktivierungsobjekt aus dem Kontext der Ausführung der externen Funktion, gefolgt vom globalen Objekt.

Wenn der Blockausdruck der with- Anweisung abgeschlossen ist, wird der Gültigkeitsbereich des Ausführungskontexts wiederhergestellt (das y- Objekt wird gelöscht), aber das Funktionsobjekt wurde zu diesem Zeitpunkt erstellt und seiner [[scope]] -Eigenschaft wurde eine Verknüpfung zur Gültigkeitsbereichskette mit dem y- Objekt oben zugewiesen .

Auflösung des Bezeichnernamens


Identifikatoren werden durch eine Reihe von Bereichen durchsucht . ECMA 262 definiert dies eher als ein Schlüsselwort als eine Kennung, die nicht unangemessen ist bestimmt wird , in Abhängigkeit von den Werten von diesen , ist es in dem Ausführungskontext gespeichert ist, in dem es ohne Bezugnahme auf dienen Ketten Umfang .

Die Auflösung des Bezeichnernamens beginnt mit dem ersten Objekt in der Scope-Kette . Der Prozess findet heraus, ob es eine Eigenschaft mit einem Namen gibt, der dem Bezeichner entspricht. Aufgrund der Tatsache, dass die Scope-KetteWenn es sich um eine Objektkette handelt, deckt dieser Test die Prototypenkette dieses Objekts ab (falls vorhanden). Wenn der entsprechende Wert nicht im ersten Objekt in der Scope-Kette gefunden werden kann , wird die Suche im nächsten Objekt fortgesetzt. Und so weiter, bis eine Eigenschaft mit einem dem Bezeichner entsprechenden Namen in einem der Objekte der Kette (oder in einem ihrer Prototypen) gefunden wird oder bis die Kette der Sichtbarkeitsbereiche endet.

Die Operation für den Bezeichner erfolgt auf dieselbe Weise wie die oben beschriebenen Ausdrücke für den Zugriff auf die Eigenschaften von Objekten. Ein Objekt, das in der Bereichskette angegeben istWenn die entsprechende Eigenschaft vorhanden ist, nimmt sie beim Ausdruck des Zugriffs auf die Eigenschaft den Platz des Objekts ein, und der Bezeichner fungiert als Name der Eigenschaft dieses Objekts. Das globale Objekt befindet sich immer am Ende der Scope-Kette.

Da der Ausführungskontext des Funktionsaufrufs das Aktivierungsobjekt (Variablenobjekt) am Anfang der Kette enthält, prüfen die im Funktionskörper verwendeten Bezeichner zunächst, ob sie formalen Parametern, den Namen interner Funktionsdeklarationen oder lokalen Variablen entsprechen. Sie werden als benannte Eigenschaften des Aktivierungsobjekts (variables Objekt) berechnet.

An der Übersetzung war auch Lyudmila Likhovid beteiligt.

Verschlüsse in Javascript [Teil 2]

Jetzt auch beliebt: