西西軟件園多重安全檢測(cè)下載網(wǎng)站、值得信賴的軟件下載站!
軟件
軟件
文章
搜索

首頁(yè)編程開發(fā)javascript|JQuery → JavaScript學(xué)習(xí)之旅——從Scope Chain到Closure

JavaScript學(xué)習(xí)之旅——從Scope Chain到Closure

相關(guān)軟件相關(guān)文章發(fā)表評(píng)論 來源:本站整理時(shí)間:2010/9/5 20:48:06字體大小:A-A+

作者:佚名點(diǎn)擊:372次評(píng)論:1次標(biāo)簽: global JS

  • 類型:行業(yè)軟件大。14.6M語(yǔ)言:中文 評(píng)分:2.2
  • 標(biāo)簽:
立即下載

a = 1;
function Outer(x){
      function Inner(y){return x + y;}
      return Inner
}
var inner = Outer(1);
inner(2);

執(zhí)行上面這段代碼的過程中,有哪些事情發(fā)生?Inner函數(shù)為什么可以引用Outer函數(shù)的參數(shù)x?closure是怎么實(shí)現(xiàn)的?本文試圖回答這些問題。

術(shù)語(yǔ)

本文雖然所講理論并不復(fù)雜,但用到不少名詞,初讀時(shí)相對(duì)比較晦澀,下面列出術(shù)語(yǔ)和簡(jiǎn)短解釋,便于閱讀時(shí)隨時(shí)查看。

  • global:engine預(yù)先創(chuàng)建好的一個(gè)object,里面有所有built-in objects的屬性。
  • globalContext:本文術(shù)語(yǔ),用作表示全局的execution context。
  • globalScopeChain:本文術(shù)語(yǔ),用作表示全局的execution context所擁有的Scope Chain,里面只有一個(gè)對(duì)象為global,用代碼表示為 [global]
  • functionContext:本文術(shù)語(yǔ),用作表示執(zhí)行函數(shù)代碼時(shí),進(jìn)入的新的execution context。
  • VariableObject:ECMAScript術(shù)語(yǔ),在globalContext中即為global,在functionContext中是被創(chuàng)建的一個(gè)對(duì)象。在進(jìn)入context時(shí),被放到scope chain的最前方。
  • outerVariable:本文術(shù)語(yǔ),表示進(jìn)入OuterFunctionContext時(shí)被創(chuàng)建的Variable Object。
  • innerVariable:本文術(shù)語(yǔ),表示進(jìn)入InnerFunctionContext時(shí)被創(chuàng)建的Variable Object。
  • outerFunctionContext:本文術(shù)語(yǔ),用作表示執(zhí)行Outer這個(gè)函數(shù)時(shí),進(jìn)入的execution context。
  • outerScopeChain:本文術(shù)語(yǔ),用作表示outerFunctionContext所擁有的Scope Chain。可用[outerVariable, global]表示。
  • innerFunctionContext:本文術(shù)語(yǔ),用作表示執(zhí)行Inner這個(gè)函數(shù)時(shí),進(jìn)入的execution context。
  • innerScopeChain:本文術(shù)語(yǔ),用作表示innerFunctionContext所擁有的Scope Chain?捎肹innerVariable, outerVariable, global]表示。

 JS代碼種類

JS代碼分三種:

  1. Global code,全局代碼
  2. Functioncode,函數(shù)內(nèi)的代碼。
  3. Eval code,為簡(jiǎn)單計(jì),不在本文說明。

Execution context

任何一句JS代碼,都是執(zhí)行在一個(gè)特定的“execution context”下面。

執(zhí)行Global code時(shí),JavaScript engine將會(huì)創(chuàng)建一個(gè)全局的context,為表述簡(jiǎn)單,我們把它叫做globalContext。

而每次進(jìn)入Functioncode時(shí),將會(huì)創(chuàng)建一個(gè)新的context,在函數(shù)返回(或有未捕獲的異常發(fā)生)時(shí),退出這個(gè)新的context,本文把它叫做functionContext。

 a = 1; //進(jìn)入globalContext

function Outer(x){
function Inner(y){return x + y;}
return Inner
} //在globalContext中創(chuàng)建Outer這個(gè)Function

var inner = Outer(1); //執(zhí)行Outer函數(shù)時(shí)進(jìn)入新創(chuàng)建的outerFunctionContext上下文。

            //然后退出,回到globalContext,把Outer(1)的返回值賦給inner這個(gè)變量。
inner(2); //進(jìn)入InnerContext,執(zhí)行Inner函數(shù)的return x + y,然后退出,回到globalContext

Scope Chain

每個(gè)execution context都有一個(gè)關(guān)聯(lián)的Scope Chain。所謂Scope Chain,其實(shí)就是一個(gè)List,里面有若干個(gè)object。

 global

globalContext所關(guān)聯(lián)的Scope Chain,這里不妨稱之為globalScopeChain,這個(gè)chain里面只有一個(gè)object,就是global,global是一個(gè)engine事先創(chuàng)建好的對(duì)象,所有的built-in Object(比如Function()、Object()、Math)都會(huì)作為這個(gè)global對(duì)象的屬性。

 Function型對(duì)象的[[Scope]]屬性

在第一篇?jiǎng)?chuàng)建Function型對(duì)象的步驟里,第5步說了,會(huì)為這個(gè)Function型對(duì)象創(chuàng)建一個(gè)[[Scope]]屬性,不過當(dāng)初沒有提到,這個(gè)屬性的值是當(dāng)前context的Scope Chain。

Outer函數(shù)是在globalContext下創(chuàng)建起來的,因此Outer.[[Scope]] = globalScopeChain,也就是[global]。而Inner函數(shù)是在執(zhí)行Outer函數(shù)時(shí),也就是在outerFunctionContext下創(chuàng)建起來的,因此Inner.[[Scope]] = OuterContext的ScopeChain,是什么呢,往下看。

 Entering execution context

每次進(jìn)入一個(gè)context(不管是globaContext還是functionContext)時(shí),都會(huì)有一系列的事情發(fā)生。

  1. 上面說到,每個(gè)context都有一個(gè)關(guān)聯(lián)的Scope Chain,這個(gè)Scope Chain就是在此時(shí)會(huì)被創(chuàng)建起來的。
  2. 確定或創(chuàng)建一個(gè)Variable Object(ECMAScript術(shù)語(yǔ)),并把它放到Scope Chain的最前面。
    對(duì)于globalContext,這個(gè)Variable Object就是global,被放到globalScopeChain里(也是globalScopeChain里唯一的一個(gè)對(duì)象);
    而如果進(jìn)入到一個(gè)functionContext,則會(huì)創(chuàng)建一個(gè)Variable Object起來,也放到Scope Chain的最前面,并且還會(huì)額外再做一件事——就是把當(dāng)前Function的[[Scope]]里所有object,放到Scope Chain里面。因此執(zhí)行Outer函數(shù)時(shí),Scope Chain是這樣的:[outerVariable, global];上面知道,創(chuàng)建Inner函數(shù)時(shí),這個(gè)Chain將作為Inner函數(shù)的[[Scope]]屬性,因此進(jìn)入Inner函數(shù)的執(zhí)行時(shí),它的Scope Chain就是[innerVariable, outerScopeChain],也就是[innerVariable, outerVariable, global]。
  3. 實(shí)例化Variable Object,就是為Variable Object創(chuàng)建一些屬性。
    首先,如果是functionContext,則把函數(shù)的參數(shù)作為Variable Object的屬性;
    其次,把聲明的函數(shù)作為Variable Object的屬性;這里的屬性將覆蓋上面的同名屬性。
    再次,把聲明的變量作為Variable Object的屬性,屬性的初始值均為undefined,只有在執(zhí)行賦值語(yǔ)句后,才會(huì)有值。這邊的屬性不會(huì)覆蓋上面的同名屬性。
  4. 為當(dāng)前context確定this,this在context中是不變的。
    詳細(xì)見下面的注解。

//在執(zhí)行一切代碼之前,進(jìn)入globalContext,global對(duì)象也已經(jīng)創(chuàng)建好。
 

//1.然后創(chuàng)建Scope Chain
globalContext.ScopeChain = [];

//2.確定variable object為global,并加入到scope chain中
variable = global;
globalContext.ScopeChain.push(global);

//3.實(shí)例化variable object,創(chuàng)建a、Outer和inner三個(gè)屬性,初始值為null。
variable.a = null;
variable.Outer = null;
variable.inner = null;

//4.確定this,在globalContext中為global。
this = global;

//以上是進(jìn)入globalContext時(shí)所做的事情  
//以下開始執(zhí)行代碼。
  a = 1;   function Outer(x){
  function Inner(y){return x + y;}
  return Inner
  }    //對(duì)于以上這段代碼,發(fā)生的事用偽代碼表示如下:
//創(chuàng)建Outer函數(shù),傳入當(dāng)前的scope chain,即[global]
Outer = new Function('', '' [global])
//為Outer.[[Scope]]賦值
Outer.[[Scope]] = [];
Outer.[[Scope]].push(global);
//這時(shí)variable的屬性O(shè)uter就指向這個(gè)函數(shù)了,不再是null。
variable.Outer = Outer

  var inner = Outer(1); //這段代碼用偽代碼表示如下:
//執(zhí)行Outer函數(shù),進(jìn)入新創(chuàng)建的outerFunctionContext上下文
//1.創(chuàng)建ouerFunctionContext的Scope Chain,并放入Outer函數(shù)的[[Scope]]力所有的object
outerFunctionContext.ScopeChain = [];
outerFunctionContext.ScopeChain.push(global) //global是[[Scope]]里唯一的對(duì)象。
//2.創(chuàng)建Variable Object屬性,并放到Scope Chain的最前方。
outerVariable= {arguments: xxx} //創(chuàng)建的variable有arguments等屬性
outerFunctionContext.ScopeChain.push(variable)
//3.實(shí)例化variable object
outerVariable.x = 1
outerVariable.Inner = new Function('y', 'return x + y', [outerVariable, global])   //注意上句創(chuàng)建Inner函數(shù)時(shí),會(huì)傳入當(dāng)前的Scope Chain,即[outerVariable, global]  //4.確定Outer函數(shù)體內(nèi)的this參數(shù),就是新創(chuàng)建的函數(shù)對(duì)象。
//最后回到globalContext中,把新建的Inner函數(shù)對(duì)象,返回給inner變量。

  inner(2); //最后執(zhí)行的這句代碼,將創(chuàng)建并進(jìn)入InnerContext。

初步結(jié)論

現(xiàn)在已經(jīng)知道,執(zhí)行Outer函數(shù)時(shí),對(duì)應(yīng)的outerScopeChain的圖如下,注意global對(duì)象忽略了指向所有built-in object的屬性:

  

執(zhí)行Inner函數(shù)時(shí),對(duì)應(yīng)的innerScopeChain的圖如下:

  

Scope Chain的作用

Scope chain的圖出來了,那么它用來干嘛呢?執(zhí)行inner函數(shù)的return x + y,會(huì)發(fā)現(xiàn),我們需要兩個(gè)變量,x和y。那么JavaScript將循著Scope Chain來查找,與__proto__鏈配合,也就是首先在innerVariable(以及其__proto__鏈)找x,沒找到,則到outerVariable中找x,找到為1。 找y時(shí)類似。這就是Inner函數(shù)體中,可以訪問得到Outer函數(shù)中定義的參數(shù)x的原理所在,不難想象,如果Outer函數(shù)中定義了局部變量z,那么z也會(huì)出現(xiàn)在outerVariable對(duì)象中,因此同樣可以被Inner函數(shù)訪問。內(nèi)部函數(shù)可以引用外部函數(shù)的參數(shù)以及變量,這就是JavaScript傳說中的閉包(Closure)。

    相關(guān)評(píng)論

    閱讀本文后您有什么感想? 已有人給出評(píng)價(jià)!

    • 8 喜歡喜歡
    • 3 頂
    • 1 難過難過
    • 5 囧
    • 3 圍觀圍觀
    • 2 無聊無聊

    熱門評(píng)論

    最新評(píng)論

    發(fā)表評(píng)論 查看所有評(píng)論(1)

    昵稱:
    表情: 高興 可 汗 我不要 害羞 好 下下下 送花 屎 親親
    字?jǐn)?shù): 0/500 (您的評(píng)論需要經(jīng)過審核才能顯示)