摘要:全局環(huán)境是作用域鏈的終點(diǎn)。環(huán)境記錄類型定義了兩種環(huán)境記錄類型聲明式環(huán)境記錄和對象環(huán)境記錄聲明式環(huán)境記錄聲明式環(huán)境記錄是用來處理函數(shù)作用域中出現(xiàn)的變量,函數(shù),形參等。變量環(huán)境變量環(huán)境就是存儲上下文中的變量和函數(shù)的。解析的過程如下
原文
ECMA-262-5 in detail. Chapter 3.2. Lexical environments: ECMAScript implementation.
簡介在之前的3.1章。我們討論了詞法環(huán)境的整體理論。我們還特別討論了與之相關(guān)的靜態(tài)作用域(static scope)和閉包(closures)。我們還提到ECMAScript所采用的鏈?zhǔn)江h(huán)境幀模型(the model of chained environment frames)。在這一章,我們將用ECMAScript去實現(xiàn)詞法環(huán)境(lexical environments)。我們要關(guān)注實現(xiàn)過程中的結(jié)構(gòu)和術(shù)語是如何體現(xiàn)這個普遍的理論。我們先從定義開始。盡管之前我們已經(jīng)給出了詞法環(huán)境在普遍理論中的定義,在這里我們給出在ECMA-262-5中標(biāo)準(zhǔn)的定義。
定義在我們之前提到的理論中,環(huán)境是用來管理嵌套的代碼塊中的數(shù)據(jù),例如變量,函數(shù)等等。在ECMAScript中也是如此。
在ECMAScript的代碼中,詞法環(huán)境依據(jù)詞法上嵌套的結(jié)構(gòu)來確定標(biāo)識符與變量值和函數(shù)的關(guān)聯(lián)。我們也提到過這種名稱與值的關(guān)聯(lián)叫做綁定。在ECMAScript中,詞法環(huán)境由兩部分構(gòu)成:一條環(huán)境記錄(environment)和一個指向外部環(huán)境的引用。環(huán)境的定義與我們之前討論的模型中的幀(frame)相對應(yīng)。因此,一條環(huán)境記錄記錄這個詞法環(huán)境中創(chuàng)建的標(biāo)識符的綁定。換句話說,一條環(huán)境記錄保存了出現(xiàn)在上下文中的變量。
考慮下面這個例子
var x = 10; function foo() { var y = 20; }
于是我們有了兩個抽象的環(huán)境,分別對應(yīng)中全局上下文和foo函數(shù)的上下文:
// 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引用是用來鏈接當(dāng)前環(huán)境和父環(huán)境的。父環(huán)境當(dāng)然也有自己的outer鏈接。全局環(huán)境的外部鏈接被設(shè)為null。全局環(huán)境是作用域鏈(chain of scopes)的終點(diǎn)。這讓人想起原型繼承是如何在ECMAScript中工作的。如果在對象本身上沒有發(fā)現(xiàn)屬性,就會去查找該對象的原型,若沒有就是原型的原型,直到原型連的終點(diǎn)。環(huán)境和這個一樣,上下文中出現(xiàn)的變量或標(biāo)識符代表屬性,外部鏈接代表指向原型的引用。一個詞法環(huán)境可能包裹多個內(nèi)部的詞法環(huán)境。例如,一個函數(shù)內(nèi)部有兩個函數(shù),那么內(nèi)部的函數(shù)的詞法環(huán)境的外部環(huán)境就是包裹它們的函數(shù)。
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 }環(huán)境記錄類型
ECMAScript定義了兩種環(huán)境記錄類型:聲明式環(huán)境記錄(declarative environment records)和對象環(huán)境記錄(object environment records)
聲明式環(huán)境記錄聲明式環(huán)境記錄是用來處理函數(shù)作用域中出現(xiàn)的變量,函數(shù),形參等。例如
// all: "a", "b" and "c" // bindings are bindings of // a declarative record function foo(a) { var b = 10; function c() {} }
在大多數(shù)場合中,聲明記錄保存綁定被認(rèn)為是在底層實現(xiàn)的。這是和ES3中活動對象概念的主要的不同。換句話說,不要求聲明記錄被當(dāng)作一個普通對象的方式來實現(xiàn),那樣很低效。這意味著聲明式環(huán)境記錄并被直接暴露給用戶,我們無權(quán)訪問這些綁定,即記錄的屬性。實際上,我們以前也不可以,即使在ES3中,我們也無法直接訪問活動對象。潛在的,聲明式記錄允許采用詞法地址技術(shù)(lexical addressing technique),這能夠直接去訪問需要的變量,而不用去作用域鏈上查找,無論作用域嵌套的有多深。ES5的標(biāo)準(zhǔn)文檔里并沒有直接提到這個事實。我們要用聲明式環(huán)境記錄替換舊的活動對象的概念,它們的實現(xiàn)效率就不一樣。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”.
一條聲明式環(huán)境記錄可以這樣表現(xiàn)
environment = { // storage environmentRecord: { type: "declarative", // storage }, // reference to the parent environment outer: <...> };對象環(huán)境記錄
相比之下,對象環(huán)境記錄是用來確定全局環(huán)境和with聲明中出現(xiàn)的變量和函數(shù)的。它們被當(dāng)作普通對象來實現(xiàn),效率低。在這樣的上下文中,用來存儲綁定的對象叫綁定對象(binding object)。在全局環(huán)境下,變量被綁定來全局對象上。
var a = 10; console.log(a); // 10 // "this" in the global context // is the global object itself console.log(this.a); // 10
一條對象環(huán)境記錄可以這樣表現(xiàn)
environment = { // storage environmentRecord: { type: "object", bindingObject: { // storage } }, // reference to the parent environment outer: <...> };執(zhí)行環(huán)境的結(jié)構(gòu)
在這里,我們簡單介紹下ES5中執(zhí)行上下文的結(jié)構(gòu)。與ES3有些不同,它有以下屬性:
ExecutionContextES5 = { ThisBinding:this綁定, VariableEnvironment: { ... }, LexicalEnvironment: { ... }, }
在全局環(huán)境中,this仍然是全局對象本身
(function (global) { global.a = 10; })(this); console.log(a); // 10
在環(huán)境對象中,this仍然取決于函數(shù)是怎樣被調(diào)用的。如果被引用調(diào)用(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"變量環(huán)境
變量環(huán)境就是存儲上下文中的變量和函數(shù)的。當(dāng)我們進(jìn)入一個函數(shù)環(huán)境中,arguments對象就被創(chuàng)建,保存來形參的值。
function foo(a) { var b = 20; } foo(10);
它的變量環(huán)境
fooContext.VariableEnvironment = { environmentRecord: { arguments: {0: 10, length: 1, callee: foo}, a: 10, b: 20 }, outer: globalEnvironment };詞法環(huán)境
[譯者注:額,變量環(huán)境的拷貝,與with有關(guān),不譯了]
閉包保存來創(chuàng)造它的上下文的詞法環(huán)境。
標(biāo)識符解析是依據(jù)詞法環(huán)境決定上下文中標(biāo)識符的綁定。換句話說,它就是作用域的查找。與上文中提到的原型鏈類似。
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
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/89588.html
摘要:調(diào)用棧意味著中的執(zhí)行環(huán)境棧,激活記錄意味著的激活對象。此外,所有的函數(shù)是一等公民。換句話說,自由變量并不存在自身的環(huán)境中,而是周圍的環(huán)境中。值得注意的是,函數(shù)并沒有用到自由變量。在后面的情形中,我們將綁定對象稱為環(huán)境幀。 原文 ECMA-262-5 in detail. Chapter 3.1. Lexical environments: Common Theory. 簡介 在這一章,...
摘要:深入系列第六篇,本篇我們追根溯源,從規(guī)范解讀在函數(shù)調(diào)用時到底是如何確定的。因為我們要從規(guī)范開始講起。規(guī)范類型包括和。下一篇文章深入之執(zhí)行上下文深入系列深入系列目錄地址。如果有錯誤或者不嚴(yán)謹(jǐn)?shù)牡胤?,請?wù)必給予指正,十分感謝。 JavaScript深入系列第六篇,本篇我們追根溯源,從 ECMAScript5 規(guī)范解讀 this 在函數(shù)調(diào)用時到底是如何確定的。 前言 在《JavaScript...
摘要:不過到底是怎麼保留的另外為什麼一個閉包可以一直使用區(qū)域變數(shù),即便這些變數(shù)在該內(nèi)已經(jīng)不存在了為了解開閉包的神秘面紗,我們將要假裝沒有閉包這東西而且也不能夠用嵌套來重新實作閉包。 原文出處: 連結(jié) 話說網(wǎng)路上有很多文章在探討閉包(Closures)時大多都是簡單的帶過。大多的都將閉包的定義濃縮成一句簡單的解釋,那就是一個閉包是一個函數(shù)能夠保留其建立時的執(zhí)行環(huán)境。不過到底是怎麼保留的? 另外...
摘要:嵌套函數(shù)在一個函數(shù)中創(chuàng)建另一個函數(shù),稱為嵌套。這在很容易實現(xiàn)嵌套函數(shù)可以訪問外部變量,幫助我們很方便地返回組合后的全名。更有趣的是,嵌套函數(shù)可以作為一個新對象的屬性或者自己本身被。 來源于 現(xiàn)代JavaScript教程閉包章節(jié)中文翻譯計劃本文很清晰地解釋了閉包是什么,以及閉包如何產(chǎn)生,相信你看完也會有所收獲 關(guān)鍵字Closure 閉包Lexical Environment 詞法環(huán)境En...
摘要:引用一個的提問個人覺得總結(jié)的比較好的兩句話原文地址另外,附上中對閉包的講解閉包中文對于閉包的簡要概括原文原文地址匿名函數(shù)和閉包來自文章作者版權(quán)聲明自由轉(zhuǎn)載非商用非衍生保持署名創(chuàng)意共享許可證轉(zhuǎn)載請注明出處 引用一個stackoverflow的提問 個人覺得總結(jié)的比較好的兩句話: An anonymous function is just a function that has no na...
閱讀 1733·2021-11-11 10:58
閱讀 4225·2021-09-09 09:33
閱讀 1272·2021-08-18 10:23
閱讀 1558·2019-08-30 15:52
閱讀 1637·2019-08-30 11:06
閱讀 1883·2019-08-29 14:03
閱讀 1520·2019-08-26 14:06
閱讀 2976·2019-08-26 10:39