摘要:全局環境是作用域鏈的終點。環境記錄類型定義了兩種環境記錄類型聲明式環境記錄和對象環境記錄聲明式環境記錄聲明式環境記錄是用來處理函數作用域中出現的變量,函數,形參等。變量環境變量環境就是存儲上下文中的變量和函數的。解析的過程如下
原文
ECMA-262-5 in detail. Chapter 3.2. Lexical environments: ECMAScript implementation.
簡介在之前的3.1章。我們討論了詞法環境的整體理論。我們還特別討論了與之相關的靜態作用域(static scope)和閉包(closures)。我們還提到ECMAScript所采用的鏈式環境幀模型(the model of chained environment frames)。在這一章,我們將用ECMAScript去實現詞法環境(lexical environments)。我們要關注實現過程中的結構和術語是如何體現這個普遍的理論。我們先從定義開始。盡管之前我們已經給出了詞法環境在普遍理論中的定義,在這里我們給出在ECMA-262-5中標準的定義。
定義在我們之前提到的理論中,環境是用來管理嵌套的代碼塊中的數據,例如變量,函數等等。在ECMAScript中也是如此。
在ECMAScript的代碼中,詞法環境依據詞法上嵌套的結構來確定標識符與變量值和函數的關聯。我們也提到過這種名稱與值的關聯叫做綁定。在ECMAScript中,詞法環境由兩部分構成:一條環境記錄(environment)和一個指向外部環境的引用。環境的定義與我們之前討論的模型中的幀(frame)相對應。因此,一條環境記錄記錄這個詞法環境中創建的標識符的綁定。換句話說,一條環境記錄保存了出現在上下文中的變量。
考慮下面這個例子
var x = 10; function foo() { var y = 20; }
于是我們有了兩個抽象的環境,分別對應中全局上下文和foo函數的上下文:
// environment of the global context globalEnvironment = { environmentRecord: { // built-ins: Object: function, Array: function, // etc ... // our bindings: x: 10 }, outer: null // no parent environment } // environment of the "foo" function fooEnvironment of the "foo" function fooEnvironment = { environmentRecord: { y: 20 }, outer: globalEnvironment }
outer引用是用來鏈接當前環境和父環境的。父環境當然也有自己的outer鏈接。全局環境的外部鏈接被設為null。全局環境是作用域鏈(chain of scopes)的終點。這讓人想起原型繼承是如何在ECMAScript中工作的。如果在對象本身上沒有發現屬性,就會去查找該對象的原型,若沒有就是原型的原型,直到原型連的終點。環境和這個一樣,上下文中出現的變量或標識符代表屬性,外部鏈接代表指向原型的引用。一個詞法環境可能包裹多個內部的詞法環境。例如,一個函數內部有兩個函數,那么內部的函數的詞法環境的外部環境就是包裹它們的函數。
function foo() { var x = 10; function bar() { var y = 20; console.log(x + y); // 30 } function baz() { var z = 30; console.log(x + z); // 40 } } // ----- Environments ----- // "foo" environment fooEnvironment = { environmentRecord: {x: 10}, outer: globalEnvironment }; // both "bar" and "baz" have the same outer // environment -- the environment of "foo" barEnvironment = { environmentRecord: {y: 20}, outer: fooEnvironment }; bazEnvironment = { environmentRecord: {z: 30}, outer: fooEnvironment }環境記錄類型
ECMAScript定義了兩種環境記錄類型:聲明式環境記錄(declarative environment records)和對象環境記錄(object environment records)
聲明式環境記錄聲明式環境記錄是用來處理函數作用域中出現的變量,函數,形參等。例如
// all: "a", "b" and "c" // bindings are bindings of // a declarative record function foo(a) { var b = 10; function c() {} }
在大多數場合中,聲明記錄保存綁定被認為是在底層實現的。這是和ES3中活動對象概念的主要的不同。換句話說,不要求聲明記錄被當作一個普通對象的方式來實現,那樣很低效。這意味著聲明式環境記錄并被直接暴露給用戶,我們無權訪問這些綁定,即記錄的屬性。實際上,我們以前也不可以,即使在ES3中,我們也無法直接訪問活動對象。潛在的,聲明式記錄允許采用詞法地址技術(lexical addressing technique),這能夠直接去訪問需要的變量,而不用去作用域鏈上查找,無論作用域嵌套的有多深。ES5的標準文檔里并沒有直接提到這個事實。我們要用聲明式環境記錄替換舊的活動對象的概念,它們的實現效率就不一樣。Brendan Eich也提到
the activation object implementation in ES3 was just “a bug”: “I will note that there are some real improvements in ES5, in particular to Chapter 10 which now uses declarative binding environments. ES1-3’s abuse of objects for scopes (again I’m to blame for doing so in JS in 1995, economizing on objects needed to implement the language in a big hurry) was a bug, not a feature”.
一條聲明式環境記錄可以這樣表現
environment = { // storage environmentRecord: { type: "declarative", // storage }, // reference to the parent environment outer: <...> };對象環境記錄
相比之下,對象環境記錄是用來確定全局環境和with聲明中出現的變量和函數的。它們被當作普通對象來實現,效率低。在這樣的上下文中,用來存儲綁定的對象叫綁定對象(binding object)。在全局環境下,變量被綁定來全局對象上。
var a = 10; console.log(a); // 10 // "this" in the global context // is the global object itself console.log(this.a); // 10
一條對象環境記錄可以這樣表現
environment = { // storage environmentRecord: { type: "object", bindingObject: { // storage } }, // reference to the parent environment outer: <...> };執行環境的結構
在這里,我們簡單介紹下ES5中執行上下文的結構。與ES3有些不同,它有以下屬性:
ExecutionContextES5 = { ThisBinding:this綁定, VariableEnvironment: { ... }, LexicalEnvironment: { ... }, }
在全局環境中,this仍然是全局對象本身
(function (global) { global.a = 10; })(this); console.log(a); // 10
在環境對象中,this仍然取決于函數是怎樣被調用的。如果被引用調用(called with a reference), 那么這個引用的所有者(the base value of the reference)就是這個this。
var foo = { bar: function () { console.log(this); } }; // --- Reference cases --- // with a reference foo.bar(); // "this" is "foo" - the base var bar = foo.bar; // with the reference bar(); // "this" is the global, implicit base this.bar(); // the same, explicit base, the global // with also but another reference bar.prototype.constructor(); // "this" is "bar.prototype"變量環境
變量環境就是存儲上下文中的變量和函數的。當我們進入一個函數環境中,arguments對象就被創建,保存來形參的值。
function foo(a) { var b = 20; } foo(10);
它的變量環境
fooContext.VariableEnvironment = { environmentRecord: { arguments: {0: 10, length: 1, callee: foo}, a: 10, b: 20 }, outer: globalEnvironment };詞法環境
[譯者注:額,變量環境的拷貝,與with有關,不譯了]
閉包保存來創造它的上下文的詞法環境。
標識符解析是依據詞法環境決定上下文中標識符的綁定。換句話說,它就是作用域的查找。與上文中提到的原型鏈類似。
var a = 10; (function foo() { var b = 20; (function bar() { var c = 30; console.log(a + b + c); // 60 })(); })();
解析a的過程如下
function resolveIdentifier(lexicalEnvironment, identifier) { // if it"s the final link, and we didn"t find // anything, we have a case of a reference error if (lexicalEnvironment == null) { throw ReferenceError(identifier + " is not defined"); } // return the binding (reference) if it exists; // later we"ll be able to get the value from the reference if (lexicalEnvironment.hasBinding(identifier)) { return new Reference(lexicalEnvironment, identifier); } // else try to find in the parent scope, // recursively analyzing the outer environment return resolveIdentifier(lexicalEnvironment.outer, identifier); } resolveIdentifier(bar.[[LexicalEnvironment]], "a") -> -- bar.[[LexicalEnvironment]] - not found, -- bar.[[LexicalEnvironment]].outer (i.e. foo.[[LexicalEnvironment]]) -> not found -- bar.[[LexicalEnvironment]].outer.outer -> found reference, value 10
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/89588.html
摘要:調用棧意味著中的執行環境棧,激活記錄意味著的激活對象。此外,所有的函數是一等公民。換句話說,自由變量并不存在自身的環境中,而是周圍的環境中。值得注意的是,函數并沒有用到自由變量。在后面的情形中,我們將綁定對象稱為環境幀。 原文 ECMA-262-5 in detail. Chapter 3.1. Lexical environments: Common Theory. 簡介 在這一章,...
摘要:深入系列第六篇,本篇我們追根溯源,從規范解讀在函數調用時到底是如何確定的。因為我們要從規范開始講起。規范類型包括和。下一篇文章深入之執行上下文深入系列深入系列目錄地址。如果有錯誤或者不嚴謹的地方,請務必給予指正,十分感謝。 JavaScript深入系列第六篇,本篇我們追根溯源,從 ECMAScript5 規范解讀 this 在函數調用時到底是如何確定的。 前言 在《JavaScript...
摘要:不過到底是怎麼保留的另外為什麼一個閉包可以一直使用區域變數,即便這些變數在該內已經不存在了為了解開閉包的神秘面紗,我們將要假裝沒有閉包這東西而且也不能夠用嵌套來重新實作閉包。 原文出處: 連結 話說網路上有很多文章在探討閉包(Closures)時大多都是簡單的帶過。大多的都將閉包的定義濃縮成一句簡單的解釋,那就是一個閉包是一個函數能夠保留其建立時的執行環境。不過到底是怎麼保留的? 另外...
摘要:嵌套函數在一個函數中創建另一個函數,稱為嵌套。這在很容易實現嵌套函數可以訪問外部變量,幫助我們很方便地返回組合后的全名。更有趣的是,嵌套函數可以作為一個新對象的屬性或者自己本身被。 來源于 現代JavaScript教程閉包章節中文翻譯計劃本文很清晰地解釋了閉包是什么,以及閉包如何產生,相信你看完也會有所收獲 關鍵字Closure 閉包Lexical Environment 詞法環境En...
摘要:引用一個的提問個人覺得總結的比較好的兩句話原文地址另外,附上中對閉包的講解閉包中文對于閉包的簡要概括原文原文地址匿名函數和閉包來自文章作者版權聲明自由轉載非商用非衍生保持署名創意共享許可證轉載請注明出處 引用一個stackoverflow的提問 個人覺得總結的比較好的兩句話: An anonymous function is just a function that has no na...
閱讀 1711·2021-11-11 10:58
閱讀 4186·2021-09-09 09:33
閱讀 1257·2021-08-18 10:23
閱讀 1548·2019-08-30 15:52
閱讀 1625·2019-08-30 11:06
閱讀 1867·2019-08-29 14:03
閱讀 1507·2019-08-26 14:06
閱讀 2943·2019-08-26 10:39