亚洲精品久久久中文字幕-亚洲精品久久片久久-亚洲精品久久青草-亚洲精品久久婷婷爱久久婷婷-亚洲精品久久午夜香蕉

您的位置:首頁(yè)技術(shù)文章
文章詳情頁(yè)

JavaScript ECMA-262-3 深入解析(二):變量對(duì)象實(shí)例詳解

瀏覽:2日期:2023-11-02 09:53:43

本文實(shí)例講述了JavaScript ECMA-262-3變量對(duì)象。分享給大家供大家參考,具體如下:

介紹

我們?cè)趧?chuàng)建應(yīng)用程序的時(shí)候,總免不了要聲明變量和函數(shù)。那么,當(dāng)我們需要使用這些東西的時(shí)候,解釋器(interpreter)是怎么樣、從哪里找到我們的數(shù)據(jù)(函數(shù),變量)的,這個(gè)過(guò)程究竟發(fā)生了什么呢?

大部分ECMAScript程序員應(yīng)該都知道變量與 執(zhí)行上下文 密切相關(guān):

var a = 10; // variable of the global context (function () { var b = 20; // local variable of the function context})(); alert(a); // 10alert(b); // 'b' is not defined

同樣,很多程序員也知道,基于當(dāng)前版本的規(guī)范,獨(dú)立作用域只能通過(guò)“函數(shù)(function)”代碼類型的執(zhí)行上下文創(chuàng)建。那么,想對(duì)于C/C++舉例來(lái)說(shuō),ECMAScript里, for 循環(huán)并不能創(chuàng)建一個(gè)局部的上下文。(譯者注:就是局部作用域):

for (var k in {a: 1, b: 2}) { alert(k);} alert(k); // variable 'k' still in scope even the loop is finished

下面我們具體來(lái)看一看,當(dāng)我們聲明數(shù)據(jù)時(shí)候的內(nèi)部細(xì)節(jié)。

數(shù)據(jù)聲明

如果變量與執(zhí)行上下文相關(guān),那么它自己應(yīng)該知道它的數(shù)據(jù)存儲(chǔ)在哪里和如何訪問(wèn)。這種機(jī)制被稱作 變量對(duì)象(variable object).

變量對(duì)象 (縮寫(xiě)為VO)就是與執(zhí)行上下文相關(guān)的對(duì)象(譯者注:這個(gè)“對(duì)象”的意思就是指某個(gè)東西),它存儲(chǔ)下列內(nèi)容:

變量 (var, VariableDeclaration); 函數(shù)聲明 (FunctionDeclaration, 縮寫(xiě)為FD); 以及函數(shù)的形參

以上均在上下文中聲明。

簡(jiǎn)單舉例如下,一個(gè)變量對(duì)象完全有可能用正常的ECMAScript對(duì)象的形式來(lái)表現(xiàn):

VO = {};

正如我們之前所說(shuō), VO就是執(zhí)行上下文的屬性(property):

activeExecutionContext = { VO: { // context data (var, FD, function arguments) }};

只有全局上下文的變量對(duì)象允許通過(guò)VO的屬性名稱間接訪問(wèn)(因?yàn)樵谌稚舷挛睦铮謱?duì)象自身就是變量對(duì)象,稍后會(huì)詳細(xì)介紹)。在其它上下文中是不可能直接訪問(wèn)到VO的,因?yàn)樽兞繉?duì)象完全是實(shí)現(xiàn)機(jī)制內(nèi)部的事情。

當(dāng)我們聲明一個(gè)變量或一個(gè)函數(shù)的時(shí)候,同時(shí)還用變量的名稱和值,在VO里創(chuàng)建了一個(gè)新的屬性。

例如:

var a = 10;function test(x) { var b = 20;};test(30);

對(duì)應(yīng)的變量對(duì)象是:

// Variable object of the global contextVO(globalContext) = { a: 10, test: };// Variable object of the 'test' function contextVO(test functionContext) = { x: 30, b: 20};

在具體實(shí)現(xiàn)層面(和在規(guī)范中)變量對(duì)象只是一個(gè)抽象的事物。(譯者注:這句話翻譯的總感覺(jué)不太順溜,歡迎您提供更好的譯文。)從本質(zhì)上說(shuō),在不同的具體執(zhí)行上下文中,VO的名稱和初始結(jié)構(gòu)都不同。

不同執(zhí)行上下文中的變量對(duì)象

對(duì)于所有類型的執(zhí)行上下文來(lái)說(shuō),變量對(duì)象的一些操作(如變量初始化)和行為都是共通的。從這個(gè)角度來(lái)看,把變量對(duì)象作為抽象的基本事物來(lái)理解更容易。而在函數(shù)上下文里同樣可以通過(guò)變量對(duì)象定義一些相關(guān)的額外細(xì)節(jié)。

JavaScript ECMA-262-3 深入解析(二):變量對(duì)象實(shí)例詳解

下面,我們?cè)敿?xì)展開(kāi)探討;

全局上下文中的變量對(duì)象

這里有必要先給全局對(duì)象(Global object)一個(gè)明確的定義:

全局對(duì)象(Global object) 是在進(jìn)入任何執(zhí)行上下文之前就已經(jīng)創(chuàng)建的對(duì)象;這個(gè)對(duì)象只存在一份,它的屬性在程序中任何地方都可以訪問(wèn),全局對(duì)象的生命周期終止于程序退出那一刻。

初始創(chuàng)建階段,全局對(duì)象通過(guò)Math,String,Date,parseInt等屬性初始化,同樣也可以附加其它對(duì)象作為屬性,其中包括可以引用全局對(duì)象自身的對(duì)象。例如,在DOM中,全局對(duì)象的window屬性就是引用全局對(duì)象自身的屬性(當(dāng)然,并不是所有的具體實(shí)現(xiàn)都是這樣):

global = { Math: <...>, String: <...> ... ... window: global};

因?yàn)槿謱?duì)象是不能通過(guò)名稱直接訪問(wèn)的,所以當(dāng)訪問(wèn)全局對(duì)象的屬性時(shí),通常忽略前綴。盡管如此,通過(guò)全局上下文的this還是有可能直接訪問(wèn)到全局對(duì)象的,同樣也可以通過(guò)引用自身的屬性來(lái)訪問(wèn),例如,DOM中的window。綜上所述,代碼可以簡(jiǎn)寫(xiě)為:

String(10); // means global.String(10);// with prefixeswindow.a = 10; // === global.window.a = 10 === global.a = 10;this.b = 20; // global.b = 20;

因此,全局上下文中的變量對(duì)象就是全局對(duì)象自身(global object itself):

VO(globalContext) === global;

準(zhǔn)確理解“全局上下文中的變量對(duì)象就是全局對(duì)象自身”是非常必要的,基于這個(gè)事實(shí),在全局上下文中聲明一個(gè)變量時(shí),我們才能夠通過(guò)全局對(duì)象的屬性間接訪問(wèn)到這個(gè)變量(例如,當(dāng)事先未知變量名時(shí)):

var a = new String(’test’);alert(a); // directly, is found in VO(globalContext): 'test'alert(window[’a’]); // indirectly via global === VO(globalContext): 'test'alert(a === this.a); // truevar aKey = ’a’;alert(window[aKey]); // indirectly, with dynamic property name: 'test' 函數(shù)上下文中的變量對(duì)象

在函數(shù)執(zhí)行上下文中,VO是不能直接訪問(wèn)的,此時(shí)由激活對(duì)象(activation object,縮寫(xiě)為AO)扮演VO的角色。

VO(functionContext) === AO;

激活對(duì)象 是在進(jìn)入函數(shù)上下文時(shí)刻被創(chuàng)建的,它通過(guò)函數(shù)的arguments屬性初始化。grguments屬性的值是Arguments object:

AO = { arguments: <ArgO>};

Arguments objects 是函數(shù)上下文里的激活對(duì)象中的內(nèi)部對(duì)象,它包括下列屬性:

callee — 指向當(dāng)前函數(shù)的引用; length — 真正傳遞的參數(shù)的個(gè)數(shù); properties-indexes (字符串類型的整數(shù)) 屬性的值就是函數(shù)的參數(shù)值(按參數(shù)列表從左到右排列)。 properties-indexes內(nèi)部元素的個(gè)數(shù)等于arguments.length. properties-indexes 的值和實(shí)際傳遞進(jìn)來(lái)的參數(shù)之間是共享的。(譯者注:共享與不共享的區(qū)別可以對(duì)比理解為引用傳遞與值傳遞的區(qū)別)

例如:

function foo(x, y, z) { alert(arguments.length); // 2 ? quantity of passed arguments alert(arguments.callee === foo); // true alert(x === arguments[0]); // true alert(x); // 10 arguments[0] = 20; alert(x); // 20 x = 30; alert(arguments[0]); // 30 // however, for not passed argument z, // related index-property of the arguments // object is not shared z = 40; alert(arguments[2]); // undefined arguments[2] = 50; alert(z); // 40}foo(10, 20);

最后一個(gè)例子的場(chǎng)景,在當(dāng)前版本的Google Chrome瀏覽器里有一個(gè)bug — 即使沒(méi)有傳遞參數(shù)z,z和arguments[2]仍然是共享的。(譯者注:我試驗(yàn)了一下,在Chrome Ver4.1.249.1059版本,該bug仍然存在)

分階段處理上下文代碼

現(xiàn)在我們終于觸及到本文的核心內(nèi)容。執(zhí)行上下文的代碼被分成兩個(gè)基本的階段來(lái)處理:

進(jìn)入執(zhí)行上下文; 執(zhí)行代碼;

變量對(duì)象的變化與這兩個(gè)階段緊密相關(guān)。

進(jìn)入執(zhí)行上下文

當(dāng)進(jìn)入執(zhí)行上下文(代碼執(zhí)行之前)時(shí),VO已被下列屬性填充滿(這些都已經(jīng)在前文描述過(guò)):

函數(shù)的所有形式參數(shù)(如果我們是在函數(shù)執(zhí)行上下文中)

— 變量對(duì)象的一個(gè)屬性,這個(gè)屬性由一個(gè)形式參數(shù)的名稱和值組成;如果沒(méi)有對(duì)應(yīng)傳遞實(shí)際參數(shù),那么這個(gè)屬性就由形式參數(shù)的名稱和undefined值組成;

所有函數(shù)聲明(FunctionDeclaration, FD)

—變量對(duì)象的一個(gè)屬性,這個(gè)屬性由一個(gè)函數(shù)對(duì)象(function-object)的名稱和值組成;如果變量對(duì)象已經(jīng)存在相同名稱的屬性,則完全替換這個(gè)屬性。

所有變量聲明(var, VariableDeclaration)

—變量對(duì)象的一個(gè)屬性,這個(gè)屬性由變量名稱和undefined值組成;如果變量名稱跟已經(jīng)聲明的形式參數(shù)或函數(shù)相同,則變量聲明不會(huì)干擾已經(jīng)存在的這類屬性。

讓我們看一個(gè)例子:

function test(a, b) { var c = 10; function d() {} var e = function _e() {}; (function x() {});}test(10); // call

當(dāng)進(jìn)入“test”函數(shù)的上下文時(shí)(傳遞參數(shù)10),AO如下:

AO(test) = { a: 10, b: undefined, c: undefined, d: <reference to FunctionDeclaration 'd'> e: undefined};

注意,AO里并不包含函數(shù)“x”。這是因?yàn)椤皒” 是一個(gè)函數(shù)表達(dá)式(FunctionExpression, 縮寫(xiě)為 FE) 而不是函數(shù)聲明,函數(shù)表達(dá)式不會(huì)影響VO(譯者注:這里的VO指的就是AO)。 不管怎樣,函數(shù)“_e” 同樣也是函數(shù)表達(dá)式,但是就像我們下面將看到的那樣,因?yàn)樗峙浣o了變量 “e”,所以它變成可以通過(guò)名稱“e”來(lái)訪問(wèn)。 FunctionDeclaration 與 FunctionExpression 的不同,將在 Chapter 5. Functions進(jìn)行詳細(xì)的探討。

這之后,將進(jìn)入處理上下文代碼的第二個(gè)階段 — 執(zhí)行代碼。

執(zhí)行代碼

這一刻,AO/VO 已經(jīng)被屬性(不過(guò),并不是所有的屬性都有值,大部分屬性的值還是系統(tǒng)默認(rèn)的初始值undefined )填滿。

還是前面那個(gè)例子, AO/VO 在代碼解釋期間被修改如下:

AO[’c’] = 10;AO[’e’] = <reference to FunctionExpression '_e'>;

再次注意,因?yàn)镕unctionExpression“_e”保存到了已聲明的變量“e”上,所以它仍然存在于內(nèi)存中(譯者注:就是還在AO/VO中的意思)。而FunctionExpression。未保存的函數(shù)表達(dá)式只有在它自己的定義或遞歸中才能被調(diào)用。 “x” 并不存在于AO/VO中。即,如果我們想嘗試調(diào)用“x”函數(shù),不管在函數(shù)定義之前還是之后,都會(huì)出現(xiàn)一個(gè)錯(cuò)誤“x is not defined”

另一個(gè)經(jīng)典例子:

alert(x); // functionvar x = 10;alert(x); // 10x = 20;function x() {};alert(x); // 20

為什么第一個(gè)alert “x” 的返回值是function,而且它還是在“x” 聲明之前訪問(wèn)的“x” 的?為什么不是10或20呢?因?yàn)椋鶕?jù)規(guī)范 — 當(dāng)進(jìn)入上下文時(shí),往VO里填入函數(shù)聲明;在相同的階段,還有一個(gè)變量聲明“x”,那么正如我們?cè)谏弦粋€(gè)階段所說(shuō),變量聲明在順序上跟在函數(shù)聲明和形式參數(shù)聲明之后,而且,在這個(gè)階段(譯者注:這個(gè)階段是指進(jìn)入執(zhí)行上下文階段),變量聲明不會(huì)干擾VO中已經(jīng)存在的同名函數(shù)聲明或形式參數(shù)聲明,因此,在進(jìn)入上下文時(shí),VO的結(jié)構(gòu)如下:

VO = {}; VO[’x’] = <reference to FunctionDeclaration 'x'> // found var x = 10;// if function 'x' would not be already defined// then 'x' be undefined, but in our case// variable declaration does not disturb// the value of the function with the same name VO[’x’] = <the value is not disturbed, still function>

隨后在執(zhí)行代碼階段,VO做如下修改:

VO[’x’] = 10;VO[’x’] = 20;

我們可以在第二、三個(gè)alert看到這個(gè)效果。

在下面的例子里我們可以再次看到,變量是在進(jìn)入上下文階段放入VO中的。(因?yàn)椋m然else部分代碼永遠(yuǎn)不會(huì)執(zhí)行,但是不管怎樣,變量“b”仍然存在于VO中。)(譯者注:變量b雖然存在于VO中,但是變量b的值永遠(yuǎn)是undefined)

if (true) { var a = 1;} else { var b = 2;}alert(a); // 1alert(b); // undefined, but not 'b is not defined' 關(guān)于變量

通常,各類文章和JavaScript相關(guān)的書(shū)籍都聲稱:“不管是使用var關(guān)鍵字(在全局上下文)還是不使用var關(guān)鍵字(在任何地方),都可以聲明一個(gè)變量”。請(qǐng)記住,這絕對(duì)是謠傳:

任何時(shí)候,變量只能通過(guò)使用var關(guān)鍵字才能聲明。

那么像下面這樣分配:

a = 10;

這僅是給全局對(duì)象創(chuàng)建了一個(gè)新屬性(但是它不是變量)。“不是變量”的意思并不是說(shuō)它不能被改變,而是指它不符合ECMAScript規(guī)范中的變量概念,所以它“不是變量”(它之所以能成為全局對(duì)象的屬性,完全是因?yàn)閂O(globalContext) === global,大家還記得這個(gè)吧?)。

讓我們通過(guò)下面的實(shí)例看看具體的區(qū)別吧:

alert(a); // undefinedalert(b); // 'b' is not definedb = 10;var a = 20;

所有根源仍然是VO和它的修改階段(進(jìn)入上下文 階段和執(zhí)行代碼 階段):

進(jìn)入上下文階段:

VO = { a: undefined};

我們可以看到,因?yàn)椤癰”不是一個(gè)變量,所以在這個(gè)階段根本就沒(méi)有“b”,“b”將只在執(zhí)行代碼階段才會(huì)出現(xiàn)(但是在我們這個(gè)例子里,還沒(méi)有到那就已經(jīng)出錯(cuò)了)。

讓我們改變一下例子代碼:

alert(a); // undefined, we know whyb = 10;alert(b); // 10, created at code executionvar a = 20;alert(a); // 20, modified at code execution

關(guān)于變量,還有一個(gè)重要的知識(shí)點(diǎn)。變量相對(duì)于簡(jiǎn)單屬性來(lái)說(shuō),變量有一個(gè)特性(attribute):{DontDelete},這個(gè)特性的含義就是不同通過(guò)delete操作符直接刪除變量屬性。

a = 10;alert(window.a); // 10alert(delete a); // truealert(window.a); // undefinedvar b = 20;alert(window.b); // 20alert(delete b); // falsealert(window.b); // still 20

但是,在eval上下文,這個(gè)規(guī)則并不起作用,因?yàn)樵谶@個(gè)上下文里,變量沒(méi)有{DontDelete}特性。

eval(’var a = 10;’);alert(window.a); // 10alert(delete a); // truealert(window.a); // undefined

使用一些調(diào)試工具(例如:Firebug)的控制臺(tái)測(cè)試該實(shí)例時(shí),請(qǐng)注意,F(xiàn)irebug同樣是使用eval來(lái)執(zhí)行控制臺(tái)里你的代碼。因此,變量屬性同樣沒(méi)有{DontDelete}特性,可以被刪除。

特殊實(shí)現(xiàn): __parent__ 屬性

前面已經(jīng)提到過(guò),按標(biāo)準(zhǔn)規(guī)范,激活對(duì)象是不可能被直接訪問(wèn)到的。但是,一些具體實(shí)現(xiàn)并沒(méi)有完全遵守這個(gè)規(guī)定,例如SpiderMonkey和Rhino;在這些具體實(shí)現(xiàn)中,函數(shù)有一個(gè)特殊的屬性 __parent__,通過(guò)這個(gè)屬性可以直接引用到函數(shù)已經(jīng)創(chuàng)建的激活對(duì)象或全局變量對(duì)象。

例如 (SpiderMonkey, Rhino):

var global = this;var a = 10;function foo() {}alert(foo.__parent__); // globalvar VO = foo.__parent__;alert(VO.a); // 10alert(VO === global); // true

在上面的例子中我們可以看到,函數(shù)foo是在全局上下文中創(chuàng)建的,所以屬性__parent__ 指向全局上下文的變量對(duì)象,即全局對(duì)象。(譯者注:還記得這個(gè)吧:VO(globalContext) === global)

然而,在SpiderMonkey中用同樣的方式訪問(wèn)激活對(duì)象是不可能的:在不同版本的SpiderMonkey中,內(nèi)部函數(shù)的__parent__ 有時(shí)指向null ,有時(shí)指向全局對(duì)象。

在Rhino中,用同樣的方式訪問(wèn)激活對(duì)象是完全可以的。

例如 (Rhino):

var global = this;var x = 10;(function foo() { var y = 20; // the activation object of the 'foo' context var AO = (function () {}).__parent__; print(AO.y); // 20 // __parent__ of the current activation // object is already the global object, // i.e. the special chain of variable objects is formed, // so-called, a scope chain print(AO.__parent__ === global); // true print(AO.__parent__.x); // 10})(); 結(jié)論

在這篇文章里,我們進(jìn)一步深入學(xué)習(xí)了跟執(zhí)行上下文相關(guān)的對(duì)象。我希望這些知識(shí)對(duì)您來(lái)說(shuō)能有所幫助,能解決一些您曾經(jīng)遇到的問(wèn)題或困惑。按照計(jì)劃,在后續(xù)的章節(jié)中,我們將探討Scope chain, Identifier resolution ,Closures。

如果您有問(wèn)題,我很高興在下面評(píng)論中解答。

英文地址 : ECMA-262-3 in detail.Chapter 2.Variable object

感興趣的朋友可以使用在線HTML/CSS/JavaScript代碼運(yùn)行工具:http://tools.jb51.net/code/HtmlJsRun測(cè)試上述代碼運(yùn)行效果。

更多關(guān)于JavaScript相關(guān)內(nèi)容可查看本站專題:《javascript面向?qū)ο笕腴T教程》、《JavaScript錯(cuò)誤與調(diào)試技巧總結(jié)》、《JavaScript數(shù)據(jù)結(jié)構(gòu)與算法技巧總結(jié)》、《JavaScript遍歷算法與技巧總結(jié)》及《JavaScript數(shù)學(xué)運(yùn)算用法總結(jié)》

希望本文所述對(duì)大家JavaScript程序設(shè)計(jì)有所幫助。

標(biāo)簽: JavaScript
相關(guān)文章:
主站蜘蛛池模板: 九九久久久久久久爱 | 国产精品成 | 国产馆在线观看视频 | 久久精品99成人中文字幕880 | 色综合久久天天综线观看 | 牛牛影院成人免费网页 | 伊人热人久久中文字幕 | 精品国产福利在线 | 2020久久精品永久免费 | 18到20岁女人毛片一区 | 免费二级c片在线观看a | 激情亚洲视频 | 国产亚洲精品成人久久网站 | 一级特级片 | 真实国产乱子伦对白视频37p | 日本国产在线观看 | chinese国产一区二区 | 中文字幕在线观看一区二区三区 | 成人免费高清视频网址 | 国产日韩欧美精品一区 | 高清影院在线欧美人色 | www色婷婷 | 日本三级理论 | 日鲁夜鲁天天鲁视频 | 亚洲激情在线看 | 日本中文字幕乱码免费 | 欧美一级做a爰片久毛片 | 亚洲国产精品va在线观看麻豆 | 亚洲色图综合网站 | 久久激情免费视频 | 黄色毛片视频在线观看 | 国产在线视频99 | 精品欧美一区二区在线观看欧美熟 | 欧美久久一区二区三区 | 麻豆视频在线免费看 | 中国一级特黄高清免费的大片 | 婷婷四房综合激情五月性色 | 一级骚片超级骚在线观看 | 日韩精品一区二区三区高清 | 欧美成人午夜精品一区二区 | 欧美一区二区三区精品国产 |