Sehen wir uns einige wichtige Funktionen im Zusammenhang mit Bereichsketten und dem Funktionsattribut [[scope]] an.
Abschlüsse
In ECMAScript stehen Abschlüsse in direktem Zusammenhang mit dem [[Umfang]] einer Funktion. Wie bereits erwähnt, wird [[Umfang]] gespeichert, wenn die Funktion erstellt wird, Live und sterben mit Funktionen. Tatsächlich ist ein Abschluss die Kombination aus dem Funktionscode und seinem [[Umfang]]. Daher umfasst [[Scope]] als eines seiner Objekte den lexikalischen Bereich (übergeordnetes Variablenobjekt), der innerhalb der Funktion erstellt wurde. Wenn die Funktion weiter aktiviert wird, wird innerhalb dieser lexikalischen Kette von Variablenobjekten (die zum Zeitpunkt der Erstellung statisch gespeichert werden) nach Variablen aus höheren Bereichen gesucht. Zum Beispiel:
var x = 10; function foo() { alert(x); } (function () { var x = 20; foo(); // 10, but not 20 })();
Wir sehen erneut, dass bei der Bezeichnerauflösung unter Verwendung des lexikalischen Bereichs, der beim Erstellen der Funktion definiert wurde, die Variable in 10 statt in 30 aufgelöst wird. Darüber hinaus zeigt dieses Beispiel auch deutlich, dass der [[Bereich]] einer Funktion (in diesem Fall einer anonymen Funktion, die von der Funktion „foo“ zurückgegeben wird) auch dann bestehen bleibt, wenn der von der Funktion erstellte Bereich abgeschlossen ist.
Weitere Details zur Theorie der Abschlüsse in ECMAScript und seinem Ausführungsmechanismus werden später erläutert.
Der [[scope]] der durch den Konstruktor erstellten Funktion
Im obigen Beispiel sehen wir, dass das [[scope]]-Attribut der Funktion erhalten wird, wenn die Funktion erstellt wird , über die Eigenschaft greift auf alle übergeordneten Kontextvariablen zu. Es gibt jedoch eine wichtige Ausnahme von dieser Regel und betrifft Funktionen, die durch Funktionskonstruktoren erstellt wurden.
var x = 10; function foo() { var y = 20; function barFD() { // 函数声明 alert(x); alert(y); } var barFE = function () { // 函数表达式 alert(x); alert(y); }; var barFn = Function('alert(x); alert(y);'); barFD(); // 10, 20 barFE(); // 10, 20 barFn(); // 10, "y" is not defined } foo();
Wir sehen, dass die über den Funktionskonstruktor erstellte Funktion „bar“ nicht auf die Variable „y“ zugreifen kann. Dies bedeutet jedoch nicht, dass die Funktion „barFn“ nicht über das Attribut [[scope]] verfügt (andernfalls kann sie nicht auf die Variable „x“ zugreifen). Das Problem besteht darin, dass das [[scope]]-Attribut einer über den Funktionskonstruktor erstellten Funktion immer das einzige globale Objekt ist. Vor diesem Hintergrund ist es mit einer solchen Funktion nicht möglich, einen anderen Kontextabschluss der obersten Ebene als den globalen zu erstellen.
Zweidimensionale Bereichskettensuche
Der wichtigste Punkt bei der Suche in der Bereichskette ist, dass die Attribute des variablen Objekts (sofern vorhanden) berücksichtigt werden müssen – abgeleitet aus dem Prototypmerkmal von ECMAScript . Wird eine Eigenschaft nicht direkt im Objekt gefunden, wird die Abfrage in der Prototypenkette fortgesetzt. Dies wird oft als zweidimensionale Kettensuche bezeichnet. (1) Zielfernrohr-Kettenglied; (2) Jede Zielfernrohrkette – geht tief in das Prototyp-Kettenglied hinein. Wir können diesen Effekt sehen, wenn die Eigenschaften in Object.prototype definiert sind.
function foo() { alert(x); } Object.prototype.x = 10; foo(); // 10
Das aktive Objekt hat keinen Prototyp, das können wir im folgenden Beispiel sehen:
function foo() { var x = 20; function bar() { alert(x); } bar(); } Object.prototype.x = 10; foo(); // 20
Wenn das aktive Objekt der Funktion „bar“-Kontext einen Prototyp hat, dann „ x“ Wird in Object.prototype geparst, da es nicht direkt in AO geparst wird. Aber im ersten Beispiel oben erreichen wir während der Identifier-Auflösung das globale Objekt (was in manchen Implementierungen nicht immer der Fall ist), das von Object.prototype erbt, und dementsprechend wird „x“ zu 10 aufgelöst.
Die gleiche Situation tritt in einigen Versionen der benannten Funktionsausdrücke (abgekürzt als NFE) von SpiderMokey auf, bei denen ein bestimmtes Objekt den optionalen Namen eines von Object.prototype geerbten Funktionsausdrucks speichert. In einigen Versionen von Blackberry ist der Das Aktivierungsobjekt zur Ausführungszeit erbt von Object.prototype.
Scope Chains in globalen und Evaluierungskontexten
Hier nicht unbedingt interessant, aber zur Erinnerung. Die Bereichskette des globalen Kontexts enthält nur globale Objekte. Der Kontext der Codeauswertung hat dieselbe Bereichskette wie der aktuelle Aufrufkontext.
globalContext.Scope = [ Global ]; evalContext.Scope === callingContext.Scope;
Die Auswirkungen auf die Bereichskette während der Codeausführung
In ECMAScript gibt es zwei Anweisungen, die die Bereichskette während der Codeausführungsphase ändern können. Dies ist die with-Anweisung und die Catch-Anweisung. Sie werden am Anfang der Bereichskette hinzugefügt und Objekte müssen unter den Bezeichnern gesucht werden, die in diesen Deklarationen erscheinen. Wenn einer dieser Fälle auftritt, wird die Bereichskette kurzzeitig wie folgt geändert:
Scope = withObject|catchObject + AO|VO + [[Scope]]
In diesem Beispiel wird das Objekt als Parameter hinzugefügt (so dass ohne das Präfix auf die Eigenschaften des Objekts zugegriffen werden kann).
var foo = {x: 10, y: 20}; with (foo) { alert(x); // 10 alert(y); // 20 }
Die Gültigkeitsbereichskette wird wie folgt geändert:
Scope = foo + AO|VO + [[Scope]]
Wir sehen erneut, dass durch die with-Anweisung die Auflösung des Bezeichners im Objekt vorne hinzugefügt wird Bereichskette:
var x = 10, y = 10; with ({x: 20}) { var x = 30, y = 30; alert(x); // 30 alert(y); // 30 } alert(x); // 10 alert(y); // 30
在进入上下文时发生了什么?标识符“x”和“y”已被添加到变量对象中。此外,在代码运行阶段作如下修改:
x = 10, y = 10;
对象{x:20}添加到作用域的前端;
在with内部,遇到了var声明,当然什么也没创建,因为在进入上下文时,所有变量已被解析添加;
在第二步中,仅修改变量“x”,实际上对象中的“x”现在被解析,并添加到作用域链的最前端,“x”为20,变为30;
同样也有变量对象“y”的修改,被解析后其值也相应的由10变为30;
此外,在with声明完成后,它的特定对象从作用域链中移除(已改变的变量“x”--30也从那个对象中移除),即作用域链的结构恢复到with得到加强以前的状态。
在最后两个alert中,当前变量对象的“x”保持同一,“y”的值现在等于30,在with声明运行中已发生改变。
同样,catch语句的异常参数变得可以访问,它创建了只有一个属性的新对象--异常参数名。图示看起来像这样:
try { ... } catch (ex) { alert(ex); }
作用域链修改为:
var catchObject = { ex: <exception object> }; Scope = catchObject + AO|VO + [[Scope]]
在catch语句完成运行之后,作用域链恢复到以前的状态。
结论
在这个阶段,我们几乎考虑了与执行上下文相关的所有常用概念,以及与它们相关的细节。按照计划--函数对象的详细分析:函数类型(函数声明,函数表达式)和闭包。顺便说一下,在这篇文章中,闭包直接与[[scope]]属性相关,但是,关于它将在合适的篇章中讨论。
以上就是JavaScript作用域链其三:作用域链特征的内容,更多相关内容请关注PHP中文网(www.php.cn)!