歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> JavaScript函數的定義與執行

JavaScript函數的定義與執行

日期:2017/3/1 9:06:55   编辑:Linux編程

要理解JavaScript函數的定義與執行,首先需要知道這幾個重要的概念,現在可以先知道稍後再理解!

函數的執行環境(excution context)、活動對象(call object)、作用域(scope)、作用域鏈(scope chain)。 接下來,我們以這個函數為例進行分析: 步驟: 1、設置作用域鏈 當定義函數a的時候,JS解釋器會將函數a的作用域鏈(scope chain)設置為“定義a時a所在的環境”,此處a第一個添加的作用域是window對象。(如果a是一個全局函數,則scope chain中只有window對象。) 個人見解:作用域鏈裡面其實是包含的活動對象,活動對象可以理解為是用來識別作用域的。(就像是一個商場分為A、B、C 三個區,就可以理解為在這個商場的作用域鏈裡面,有A、B、C 3個活動對象,3個范圍!(“個人見解,可能不恰當”)。 2、執行環境 當執行函數a的時候,a會進入相應的執行環境(excution context)。 個人見解:創建執行環境分為創建作用域創建活動對象兩步。 3、作用域 當創建執行環境的過程中,首先會為a 添加一個scope屬性,即a的作用域,其值就是第一步中的scope chain。即a.scope = a的作用域鏈。 個人見解:可以把作用域和作用域鏈理解成是名字不同但作用相同! 4、創建活動對象 創建完作用域,緊接著執行環境會創建一個活動對象(call object)。活動對象也是一個擁有屬性的對象,但它不具有原型而且不能通過javascript代碼直接訪問(可以看下面的圖或者個人見解理解)。創建完活動對象後,把活動對象添加到a的作用域鏈的最頂端。此時a的作用域鏈包含兩個對象:a的活動對象和window對象。 下一步是在活動對象上添加一個arguments屬性,它保存著調用函數a時所傳遞的參數。 最後所有的函數a的形參和內部的函數b的引用也被添加到a的活動對象上。在這一步中,完成了函數b的定義,因此如同第3步,函數b的作用域被設置為b所定義的環境,即a的作用域。(就類似於a的作用域鏈中第一個加入的作用域是window對象。)! 個人見解:(1) 活動對象是一個為了理解而添加的名詞,實際不存在,所以不具有原型、也不能用實際代碼訪問。(類似於磁感線,只是為了描述) 完結:到此,整個函數a從定義到執行的步驟就完成了。此時a返回b的引用給c,又因為函數b的作用域鏈包含了函數a的活動對象的引用,也就是說b中可以訪問到a中定義的所有變量和函數。又函數b被c引用,函數b依賴於a,因此函數a再返回後不會被GC回收(參考最下方javascript的垃圾回收機制)! 當函數b被執行的時候也會像以上步驟一樣。因此執行時b的作用域鏈包含了3個對象:b的活動對象,a的活動對象,window對象,如圖所示: 如圖所示,當在函數b訪問一個變量的時候,搜索順序是: b的活動對象 —>b的原型對象(存在的話)—>a的活動對象 —>window對象 變量查找機制:先查找自身的活動對象,如果存在則返回,如果不存在且該函數存在prototype原型對象,則查找原型對象。依次查找a的活動對象、window對象。直到找到為止,如果整個作用域鏈上沒有找到,則返回undefined。 總結:以上提到了兩個重要的詞語:函數的定義與執行。文中提到函數的作用域是在定義函數的時候就已經確定,而不是在執行的時候確定(參看步驟1和3).用一段代碼來說明這個問題:
1 function f(x){
2   var g = function(){ return x;}
3    return g;
4 }
5  var h = f(1);
6 alert(h());
這段代碼中變量h指向了f中的那個匿名函數(由g返回,也可以理解為不匿名,因為有名字g(不正規理解))。 (1)h的作用域在定義的時候確定: 個人理解h的作用域鏈:g的活動對象->f的活動對象->window對象(因為此處h代表的是返回的"g()"函數,既然是定義為准,就應該是定義"g()"函數時確定,所以 h.scope chain = g.scope chain ) 網上參考h的作用域鏈:h的活動對象 ->f的活動對象->window對象 (2)h的作用域在執行(alert( h ( ) )的時候確定: h的作用域鏈:h的活動對象->alert的活動對象->window對象。 結果:(1)應該輸出1 (2)應該輸出undefined 。 實踐證明函數的作用域是在定義這個函數的時候就已經確定了!

補充:Javascript的垃圾回收機制

在Javascript中,如果一個對象不再被引用,那麼這個對象就會被GC回收。如果兩個對象互相引用,而不再被第3者所引用,那麼這兩個互相引用的對象也會被回收。因為函數a被b引用,b又被a外的c引用,這就是為什麼函數a執行後不會被回收的原因。

function a(){

var i = 0;

var b = function(){ alert(++i);}

return b;

}

var c = a();

c();

以上代碼如果沒有return b; 當函數a執行完後就會被回收!b引用a,而b不再被第3者c所引用(沒有返回數據給c),那麼這兩個互相引用的對象也會被回收。

Copyright © Linux教程網 All Rights Reserved