一道JS前端閉包面試題解析

問題

代碼A閉包

function fun(n,o){    console.log(o);    return {
        fun:function(m){//[2]
            return fun(m,n);//[1]
        }
    }
}var a=fun(0);
a.fun(1);
a.fun(2);
a.fun(3);var b=fun(0).fun(1).fun(2).fun(3);var c=fun(0).fun(1);
c.fun(2);
c.fun(3);

求出程序輸出函數

這是一個閉包測試題測試

轉換爲等價代碼

return返回的對象的fun屬性對應一個新建的函數對象,這個函數對象將造成一個閉包做用域,使其可以訪問外層函數的變量n及外層函數fun,爲了避免將fun函數和fun屬性搞混,咱們將上述代碼修改以下:
代碼Bspa

function _fun_(n,o){
    console.log(o);
        return {
        fun:function(m){
                    return _fun_(m,n);
        }
    }
}
var a=_fun_(0);//undefined
a.fun(1);//0
a.fun(2);//0
a.fun(3);//0
var b=_fun_(0).fun(1).fun(2).fun(3);//undefined,0,1,2
var c=fun(0).fun(1);//undefined,0,
c.fun(2);//1
c.fun(3); //1

那麼就有同窗問了,爲何能夠這樣改呢,你怎麼能肯定[1]處的fun不是[2]代碼所在處的fun呢,要知道此處的fun屬性但是指向一個函數對象哦~
這裏就要說到JS的詞法做用域,JS變量做用域存在於函數體中即函數體,而且變量的做用域是在函數定義聲明的時候就是肯定的,而非在函數運行時。
以下代碼code

var name="global";
function foo(){
    console.log(name);
}
function fooOuter1(){
    var name="local";
    foo();
}
fooOuter1();//輸出global 而不是local,而且和閉包沒有任何關係function fooOuter2(){    var name="local";    function foo(){
        console.log(name);
    }
    foo();
}
fooOuter2();//輸出local 而不是global,在函數聲明是name變量做用域就在其外層函數中,嗯嗯就是閉包~

好了咱們回到題目,在函數聲明定義階段,[2]處的匿名函數進行定義聲明,發如今[1]處須要引用一個名爲fun的函數對象,那麼首先在當前函數體內尋找,發現沒有,那麼就到其外層函數-這個匿名函數的包裹函數中去查找,發現也沒有,到外層函數中去,發現外面沒有函數包裹了,那就到全局環境下去找,額偶終於找到了......就把fun函數指定爲全局環境下的fun函數對象並加入到匿名函數的閉包中去。至此咱們就知道代碼B爲何和代碼A是等價的了~~~對象

建立閉包做用域

JS在詞法分析結束後,肯定了1個閉包,就是返回的對象fun屬性對應的匿名函數的閉包-訪問全局環境下的_func_及其外層函數的函數內部變量n;
在每次_func_執行的時候,都會將閉包中變量的做用域信息傳遞到函數執行環境中,供函數執行時獲取變量值時使用作用域

執行輸出

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

_fun_函數執行,由於第2個參數未定義,輸出undefined。而後返回一個對象,帶有fun屬性,指向一個函數對象-帶有閉包,可以訪問到_fun_和變量n_
a.fun(1)執行返回的對象的fun方法,傳入m的值1,調用返回_fun_(1,0)
因此輸出爲0,a.fun(2),a.fun(3)a.fun(1)io

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

等價代碼console

var b=_fun_(0);
var b1=b.fun(1);
var b2=b1.fun(2);//[3]
var b3=b2.fun(3);//[4]

前2句和上面的輸出相同undefined,0,當[3]被調用時,b1對象中有一個閉包,引用了_fun_函數及外層函數變量n=1,因此匿名函數執行的函數調用爲_fun_(2,1),輸出結果爲1,並返回一個新的對象。
當[4]執行時,b2對象也有一個閉包,引用了_fun_函數及外層函數變量n=2,執行_fun_(3,2),輸出結果爲2function

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

能看懂前面的代碼執行解釋,理解上面的代碼執行輸出就不會有問題了

相關文章
相關標籤/搜索