關於閉包,先後文,做用域鏈

引子:關於閉包
什麼是閉包呢?
  從定義上來看,全部的函數均可以是閉包。當一個函數在調用時,引用了不是本身做用域內定義的變量(一般稱其爲自由變量),則造成了閉包;閉包是代碼塊和建立該代碼塊的上下文中數據的結合。javascript


例子:   function mytest( ){                     
           var test=10;
          return function( ){ 
                 test++;
               alert(test);
      } 
}
var atest = new mytest( ); //引用返回的函數
atest( );  // 11
atest( ); //  12
ps:注意運用閉包的常見錯誤-->for(var i,len=xx.length;i<len;i++)循環;當咱們所引用的自由變量爲i時,因爲i一直在內存中沒有釋放,因此函數每次alert(i)時,其值均爲最終的i;(例子不少,就不寫了)
    經過測試結果咱們能夠發現, 閉包會使該函數引用的變量一直在內存中,緣由是什麼呢?
簡單的解釋就是由於這個返回的函數引用了變量test;當瀏覽器解析到var atest=new mytest();這一行且mytes()函數執行完畢,準備內存釋放時,發現所返回的函數引用了test變量。從而該出棧的並無出去;
    想徹底弄清楚這個問題,咱們還須要進一步理解AO(活動對象)和VO(變量對象)以及做用域鏈、執行上下文的問題。
    那麼這整個機制瀏覽器究竟是怎麼解析的呢?
    不急,咱們來看看執行上下文、做用域鏈、活動對象和變量對象;
寫在前面的執行上下文:
 a:定義:html

  每次當控制器轉到ECMAScript可執行代碼的時候,即會進入到一個執行上下文。執行上下文(簡稱-EC)是ECMA-262標準裏的一個抽象概念,用於同可執行代碼(executable code)概念進行區分。
活動的執行上下文組在邏輯上組成一個堆棧。堆棧底部永遠都是全局上下文(global context),而頂部就是當前(活動的)執行上下文。堆棧在EC類型進入和退出上下文的時候被修改(進棧或出棧)。
b:若咱們定義執行上下文堆棧是一個數組:
ECStack = [];
//GlobalContext始終在堆棧底部,其他的FunctionContext按激活順序被壓入,結束時被彈出
ECStack = [
  globalContext
];
ECStack = [  FunctionAaContext,//函數A內部函數a可執行代碼 
FunctionAContext,//函數A可執行代碼(不包含內部函數代碼,只能對其聲明,就像在全局中只能對函數A聲明同樣,須要內部函數激活時,建立   GlobalContext                                                     出新的執行上下文環境,才能執行相應的代碼)      調用結束後出棧  
];

 

   ps:
每次進入function (即便function被遞歸調用或做爲構造函數) 的時候或者內置的eval函數工做的時候,當前的執行上下文都會被壓入堆棧;

 

 

一、做用域鏈
     首先,咱們如何建立一個做用域呢,function()。函數是javascript中惟一一個能建立出做用域的,也就是說for、if、while的{}是不能建立出做用域的。區別c++中的塊做用域{}。
     一個函數的做用域建立後,將貫穿他的始「{」,終「}」,做用域java

在函數建立時被存儲,與函數共存亡

。這句話就應該着重理解貫穿2字了,若函數內部嵌套着多個函數,那麼從最內層函數做用域依次往外就造成了做用鏈。
ps:須要咱們理解做用域鏈的變量查找機制是由內往外的。先找自身做用域,再一次往外,若沒有,則等同沒有var時的聲明(爲全局添加了一個屬性);c++

做用域鏈正是內部上下文全部變量對象(包括父變量對象)的列表


二、變量對象(Variable Object)、活動對象(Activation Object)
     瀏覽器在對代碼進行解析時,會先進行變量聲明和函數聲明以及函數形參聲明;
(全局做用域下)那麼這些優先聲明的變量儲存在哪裏呢? 
沒錯,就在變量對象中(抽象概念);活動對象怎麼理解呢?設計模式

     抽象變量對象VO (變量初始化過程的通常行爲)
    
      --> 全局上下文變量對象GlobalContextVO
          (VO === this === global)

      --> 函數上下文變量對象FunctionContextVO
           (VO === AO, 而且添加了<arguments>(形參類數組)和<formal parameters>(形參的值))數組

ps:在函數執行上下文中,VO是不能直接訪問的,此時由活動對象扮演VO的角色。Arguments對象是活動對象的一個屬性,它包括以下屬性:瀏覽器

  1. callee — 指向當前函數的引用閉包

  2. length — 真正傳遞的參數個數函數

  3. properties-indexes (字符串類型的整數) 屬性的值就是函數的參數值(按參數列表從左到右排列)。 properties-indexes內部元素的個數等於arguments.length. properties-indexes 的值和實際傳遞進來的參數之間是共享的。學習

 

 

如今讓咱們來串一下

 

一、全局執行上下文

建立global.VO

二、全局變量的賦值 | 調用函數()(激活)

激活函數後,會獲得當前的AO,其中有內部函數的聲明、內部變量的聲明、形參

三、進入所激活的函數的上下文

進行所在做用域鏈上的變量的賦值 各類運算 (做用域鏈包含全局的VO,和當前執行上下文的AO)

4.a、若在函數中有內部函數調用(或自執行),重複3;

4.b  若返回一個函數(或其引用),且該函數有對自由變量的引用-->造成閉包-->做用域鏈機制依然有效-->當前已壓入執行上下文堆棧的FunctionContext不會出棧;-->回到2;

4.c  正常return或正常結束,FunctionContext出棧;-->回到2;

5.全部代碼執行完畢,程序關閉,釋放內存。

 

 

寫在最後:

  若是你是剛開始接觸javascript,若是你也相信博客園可以讓你的學習更加規劃與深刻,我推薦你看看王福鵬老師和湯姆大叔的博文,本身仔細揣摩,記錄下本身的感想,必定會有收穫的。加油。我正在努力看深刻系列的設計模式,感受看着不是很順,有些很差理解,但願有經驗的博主們能指點指點。這是個人第一篇博文,真心但願可以在博客園中尋到良師益友。

另貼上湯姆大叔深刻javascript系列博文的地址,

http://www.cnblogs.com/TomXu/archive/2011/12/15/2288411.html
相關文章
相關標籤/搜索