<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      [JavaScript]ECMA-262-3 深入解析.第二章.變量對象

      介紹

      我們在創(chuàng)建應(yīng)用程序的時候,總免不了要聲明變量和函數(shù)。那么,當(dāng)我們需要使用這些東西的時候,解釋器(interpreter)是怎么樣、從哪里找到我們的數(shù)據(jù)(函數(shù),變量)的,這個過程究竟發(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); // 10
      alert(b); // "b" is not defined
      

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

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

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

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

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

      變量對象 (縮寫為VO)就是與執(zhí)行上下文相關(guān)的對象(譯者注:這個“對象”的意思就是指某個東西),它存儲下列內(nèi)容:
      • 變量 (var, VariableDeclaration);
      • 函數(shù)聲明 (FunctionDeclaration, 縮寫為FD);
      • 以及函數(shù)的形參

      以上均在上下文中聲明。

      簡單舉例如下,一個變量對象完全有可能用正常的ECMAScript對象的形式來表現(xiàn):

      VO = {};

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

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

      只有全局上下文的變量對象允許通過VO的屬性名稱間接訪問(因為在全局上下文里,全局對象自身就是變量對象,稍后會詳細介紹)。在其它上下文中是不可能直接訪問到VO的,因為變量對象完全是實現(xiàn)機制內(nèi)部的事情。

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

      例如:

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

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

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

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

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

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

      下面,我們詳細展開探討;

      全局上下文中的變量對象

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

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

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

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

      因為全局對象是不能通過名稱直接訪問的,所以當(dāng)訪問全局對象的屬性時,通常忽略前綴。盡管如此,通過全局上下文的this還是有可能直接訪問到全局對象的,同樣也可以通過引用自身的屬性來訪問,例如,DOM中的window。綜上所述,代碼可以簡寫為:

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

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

      VO(globalContext) === global;
      

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

      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); // true
      
      var aKey = 'a';
      alert(window[aKey]); // indirectly, with dynamic property name: "test"
       

      函數(shù)上下文中的變量對象

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

      VO(functionContext) === AO;

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

      AO = {
        arguments: <ArgO>
      };
      

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

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

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

      分階段處理上下文代碼

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

      • 進入執(zhí)行上下文;
      • 執(zhí)行代碼;

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

      進入執(zhí)行上下文

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

      • 函數(shù)的所有形式參數(shù)(如果我們是在函數(shù)執(zhí)行上下文中)
      • — 變量對象的一個屬性,這個屬性由一個形式參數(shù)的名稱和值組成;如果沒有對應(yīng)傳遞實際參數(shù),那么這個屬性就由形式參數(shù)的名稱和undefined值組成;

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

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

      讓我們看一個例子:

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

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

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

      注意,AO里并不包含函數(shù)“x”。這是因為“x” 是一個函數(shù)表達式(FunctionExpression, 縮寫為 FE) 而不是函數(shù)聲明,函數(shù)表達式不會影響VO(譯者注:這里的VO指的就是AO)。 不管怎樣,函數(shù)“_e” 同樣也是函數(shù)表達式,但是就像我們下面將看到的那樣,因為它分配給了變量 “e”,所以它變成可以通過名稱“e”來訪問。 FunctionDeclarationFunctionExpression 的不同,將在 Chapter 5. Functions進行詳細的探討。

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

      執(zhí)行代碼

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

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

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

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

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

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

      為什么第一個alert “x” 的返回值是function,而且它還是在“x” 聲明之前訪問的“x” 的?為什么不是10或20呢?因為,根據(jù)規(guī)范 — 當(dāng)進入上下文時,往VO里填入函數(shù)聲明;在相同的階段,還有一個變量聲明“x”,那么正如我們在上一個階段所說,變量聲明在順序上跟在函數(shù)聲明和形式參數(shù)聲明之后,而且,在這個階段(譯者注:這個階段是指進入執(zhí)行上下文階段),變量聲明不會干擾VO中已經(jīng)存在的同名函數(shù)聲明或形式參數(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;
       

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

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

      if (true) {
        var a = 1;
      } else {
        var b = 2;
      }
      
      alert(a); // 1
      alert(b); // undefined, but not "b is not defined"
       

      關(guān)于變量

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

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

      那么像下面這樣分配:

       a = 10; 

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

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

      alert(a); // undefined
      alert(b); // "b" is not defined
      
      b = 10;
      var a = 20;
       

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

      進入上下文階段:

      VO = {
        a: undefined
      };
       

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

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

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

      關(guān)于變量,還有一個重要的知識點。變量相對于簡單屬性來說,變量有一個特性(attribute):{DontDelete},這個特性的含義就是不同通過delete操作符直接刪除變量屬性。

      a = 10;
      alert(window.a); // 10
      
      alert(delete a); // true
      
      alert(window.a); // undefined
      
      var b = 20;
      alert(window.b); // 20
      
      alert(delete b); // false
      
      alert(window.b); // still 20
       

      但是,在eval上下文,這個規(guī)則并不起作用,因為在這個上下文里,變量沒有{DontDelete}特性。

      eval('var a = 10;');
      alert(window.a); // 10
      
      alert(delete a); // true
      
      alert(window.a); // undefined
      

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

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

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

      例如 (SpiderMonkey, Rhino):

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

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

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

      在Rhino中,用同樣的方式訪問激活對象是完全可以的。

      例如 (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é)論

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

      如果您有問題,我很高興在下面評論中解答。

      其他參考

       

      英文地址 : ECMA-262-3 in detail.Chapter 2.Variable object
      中文地址 : [JavaScript]ECMA-262-3 深入解析.第二章.變量對象

      posted @ 2010-04-23 16:42  Justin  閱讀(8869)  評論(32)    收藏  舉報
      主站蜘蛛池模板: 国产精品综合av一区二区国产馆 | 超碰成人精品一区二区三| 中文日产乱幕九区无线码| 欧美人人妻人人澡人人尤物| 亚洲欧洲精品一区二区| 四虎影视一区二区精品| 国产精品午夜av福利| 亚洲成人资源在线观看| 国内精品久久人妻无码不卡| 日韩精品一区二区蜜臀av| 日本一区二区三区免费播放视频站| 亚洲青青草视频在线播放| 国厂精品114福利电影免费| 国偷自产一区二区三区在线视频| a级国产乱理伦片在线观看al | 人妻少妇偷人一区二区| 最近中文字幕日韩有码| 色狠狠色噜噜AV一区| 亚洲人成网站观看在线观看| 国产成人MV视频在线观看| 国产成人自拍小视频在线| 日韩精品成人网页视频在线| 亚洲一级特黄大片在线观看 | 农村老熟妇乱子伦视频| 国产在线拍揄自揄拍无码| 强d乱码中文字幕熟女1000部| 无码人妻丰满熟妇啪啪欧美| 亚洲欧美在线一区中文字幕| 成全高清在线播放电视剧| 欧美色欧美亚洲另类二区| 亚洲欧洲一区二区天堂久久| 久久香蕉国产线看观看怡红院妓院| 国产精品不卡一区二区三区| 污网站在线观看视频| 99久热在线精品视频| 亚洲色大成网站www在线| 海阳市| 国产AV永久无码青青草原| 国产午夜福利高清在线观看| 国色精品卡一卡2卡3卡4卡在线| 日韩V欧美V中文在线|