深入理解JS函式作用域鏈與閉包問題

2021-09-07 17:20:13 字數 3501 閱讀 8136

function fun(n,o) 

};}var a = fun(0); a.fun(1); a.fun(2); a.fun(3);//

undefined,?,?,?

var b = fun(0).fun(1).fun(2).fun(3);//

undefined,?,?,?

var c = fun(0).fun(1); c.fun(2); c.fun(3);//

undefined,?,?,?

//問:三行a,b,c的輸出分別是什麼?

這是一道非常典型的js閉包問題。其中巢狀了三層fun函式,搞清楚每層fun的函式是那個fun函式尤為重要。可以先在紙上或其他地方寫下你認為的結果,然後展開看看正確答案是什麼?

//

答案://

a: undefined,0,0,0

//b: undefined,0,1,2

//c: undefined,0,1,1

要分析此問題,我們需要了解:

這段****現了三個fun函式,所以第一步先搞清楚,這三個fun函式的關係,哪個函式與哪個函式是相同的。

function fun(n,o) };}

先看第乙個fun函式,屬於標準具名函式宣告,是新建立的函式,他的返回值是乙個物件字面量表示式,屬於乙個新的object。這個新的物件內部包含乙個也叫fun的屬性,屬於匿名函式表示式,即fun這個屬性中存放的是乙個新建立匿名函式表示式。

注意:所有宣告的匿名函式都是乙個新函式。

所以第乙個fun函式與第二個fun函式不相同,均為新建立的函式。

再說第三個fun函式之前需要先說下,在函式表示式內部能不能訪問存放當前函式的變數。

測試1,物件內部的函式表示式:

var o=

};o.fn();

//error報錯:fn is not defined

測試2,非物件內部的函式表示式:

var fn=function ();

fn();

//function ();正確

結論是:使用 var 或是非物件內部的函式表示式內,可以訪問到存放當前函式的變數;在物件內部的則不能訪問到。

原因也非常簡單,因為函式作用域鏈的問題,採用var的是在外部建立了乙個fn變數,函式內部當然可以在內部尋找不到 fn 後向上級作用域查詢 fn,而在建立物件內部時,因為沒有在函式作用域內建立 fn,所以無法訪問

所以綜上所述,可以得知,最內層的return出去的fun函式不是第二層fun函式,是最外層的fun函式

所以,三個fun函式的關係也理清楚了,第乙個等於第三個,他們都不等於第二個。

再看下原題,現在知道了程式中有兩個fun函式(第乙個和第三個相同),遂接下來的問題是搞清楚,執行時他執行的是哪個fun函式?

1、第一行a

var a = fun(0);  a.fun(1);  a.fun(2);  a.fun(3);
可以得知,第乙個fun(0)是在呼叫第一層fun函式。第二個fun(1)是在呼叫前乙個fun的返回值的fun函式,所以:後面幾個fun(1),fun(2),fun(3),函式都是在呼叫第二層fun函式。因此:

在第一次呼叫fun(0)時,o為undefined;

第二次呼叫fun(1)時m為1,此時fun閉包了外層函式的n,也就是第一次呼叫的n=0,即m=1,n=0,並在內部呼叫第一層fun函式fun(1,0);所以o為0;

第三次呼叫fun(2)時m為2,但依然是呼叫a.fun,所以還是閉包了第一次呼叫時的n,所以內部呼叫第一層的fun(2,0);所以o為0

第四次同理;

即:最終答案為undefined,0,0,0

2、第二行b

var b = fun(0).fun(1).fun(2).fun(3);
先從fun(0)開始看,肯定是呼叫的第一層fun函式;而他的返回值是乙個物件,所以第二個fun(1)呼叫的是第二層fun函式,後面幾個也是呼叫的第二層fun函式。因此:

在第一次呼叫第一層fun(0)時,o為undefined;

第二次呼叫 .fun(1)時m為1,此時fun閉包了外層函式的n,也就是第一次呼叫的n=0,即m=1,n=0,並在內部呼叫第一層fun函式fun(1,0);所以o為0;

第三次呼叫 .fun(2)時m為2,此時當前的fun函式不是第一次執行的返回物件,而是第二次執行的返回物件。而在第二次執行第一層fun函式時時(1,0)所以n=1,o=0,返回時閉包了第二次的n,遂在第三次呼叫第三層fun函式時m=2,n=1,即呼叫第一層fun函式fun(2,1),所以o為1;然後閉包了第三次的n就為2了

第四次呼叫 .fun(3)時m為3,閉包了第三次呼叫的n,同理,最終呼叫第一層fun函式為fun(3,2);所以o為2;

即最終答案:undefined,0,1,2

3、第三行c

var c = fun(0).fun(1);  c.fun(2);  c.fun(3);
根據前面兩個例子,可以得知:

fun(0)為執行第一層fun函式,.fun(1)執行的是fun(0)返回的第二層fun函式,這裡語句結束,因此c存放的是fun(1)的返回值,而不是fun(0)的返回值,所以c中閉包的也是fun(1)第二次執行的n的值。c.fun(2)執行的是fun(1)返回的第二層fun函式,c.fun(3)執行的是fun(1)返回的第二層fun函式。因此:

在第一次呼叫第一層fun(0)時,o為undefined;

第二次呼叫 .fun(1)時m為1,此時fun閉包了外層函式的n(即n=1了),也就是第一次呼叫的n=0,即m=1,n=0,並在內部呼叫第一層fun函式fun(1,0);所以o為0;

第三次呼叫 .fun(2)時m為2,此時fun閉包的是第二次呼叫的n=1,即m=2,n=1,並在內部呼叫第一層fun函式fun(2,1);所以o為1;

第四次.fun(3)時同理,但依然是呼叫的第二次的返回值,遂最終呼叫第一層fun函式fun(3,1),所以o還為1

即最終答案:undefined,0,1,1

分析該問題主要需要了解函式作用域鏈的問題,這樣就能清楚第乙個fun其實是和第三個fun是一樣的;

其次就是需要了解閉包的知識,如果要我說什麼是閉包,我認為,廣義上的閉包就是指乙個變數在他自身作用域外被使用了,就叫發生了閉包。所以當第三個fun執行時,閉包將m的值繫結到了n上

js深入理解之作用域鏈

語法分析,分析3樣東西 第1步 先分析引數 第2步 再分析變數宣告 第3步 分析函式宣告 乙個函式能使用的區域性變數,就從上面的3步分析而來 具體步驟 0 函式執行前的1瞬間,生成 active object 活動物件 下稱ao 1 1.1 函式宣告的引數,形成ao的屬性,值全是undefined,...

深入理解JS閉包

閉包 closure 是j acript語言的乙個難點,也是它的特色,很多高階應用都要依靠閉包實現。一 變數的作用域 要理解閉包,首先必須理解j ascript特殊的變數作用域。變數的作用域無非就是兩種 全域性變數和區域性變數。j ascript語言的特殊之處,就在於函式內部可以直接讀取全域性變數。...

JS深入理解作用域 作用域鏈,變數提公升

1 作用域 1 全域性作用域 在瀏覽器載入我們html頁面的時候,首先會開闢乙個供js 執行的環境,即全域性作用域,這是乙個棧記憶體 2 私有作用域 函式執行時,開闢乙個新的棧記憶體,形成私有作用域 2 基本資料型別與引用資料型別區別?基本資料型別 string,number,boolean,nul...