1. 程式人生 > >圖解JavaScript閉包面試題

圖解JavaScript閉包面試題

由於最近在學習關於閉包相關的知識,並且閉包這個知識點讓我有點搞不太清楚其具體的定義,所以在網上也查閱了很多大佬的講解和對閉包的一個定義。

 

最後感覺還是MDN上的說法感覺比較好理解一些,對閉包還是不太理解的道友可以嘗試看一看。

MDN上是這樣說的:閉包是函式和宣告該函式的詞法環境的組合

原地址:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures

 

在瞭解閉包的過程中遇到一個很多地方都出現的一個面試題,按照自己的想法想了下發現幾乎沒對...所以就花了些時間分析了一下,供自己以後忘記了可以回顧一下。

同時,如果這裡依舊存在一些誤區,希望各位大佬們在評論區幫忙指正,感激萬分!

 

原題如下:

        function fun(n, o) {
            console.log(o);
            return {
                fun: function (m) {
                    return fun(m, n);
                }
            };
        }

        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); // ?

有興趣的道友們也可以試著先想一想答案,看下是否一致?

 

以下是解題思路:

1. 首先我對這個題畫了下題目中兩個fun函式中的一些資訊 (可能不太完整,但是解題應該夠用了)

2. 當執行 var a = fun(0); 時,記憶體中開闢了一塊新的空間給這個新的物件,這個物件中定義了一個方法fun。並且在fun(0)執行時,在當前作用域下的變數資訊如下:

 

因此,當該語句執行時,控制檯列印的值為 undefined

 

3. 當執行 a.fun(1) 時,因為物件a中的fun方法在定義時所處的環境( [[scope]] )中存在一個變數n和變數o,

並且在這條語句執行的時候,變數n( 此時n的值為0 ) 被第三方 (除了函式fun和方法fun)引用了,也就是被外部的物件a引用了,因此產生了Closure(閉包)。

然後,這條語句的return執行的時候先執行 呼叫fun函式----> 把m的值傳遞給了fun函式中的n,把n傳遞給了fun函式中的o, 因此控制檯中列印o的結果為0。

然而事情並沒有結束,呼叫fun函式會返回一個新的物件,這個物件也會在記憶體中新開闢一個空間,而此時這個新物件中的方法fun被定義時所處環境中的變數n已經被賦值為m的值,也就是1了。

4. 當a.fun(2)執行的時候,發生了和上面一樣的故事,並且記憶體中又被返回了一個新的物件且這個新物件中的方法fun被定義時所處環境中的變數n已經被賦值為m的值,也就是2

5. a.fun(3)執行同上,且這個新物件中的方法fun被定義時所處環境中的變數n已經被賦值為m的值,也就是3

故: a.fun(2) 和 a.fun(3) 在控制檯中列印o的結果都為0,且不管你傳的引數是多少,只要你沒有改變a物件的值,那麼輸出的結果都是0,因為你傳的引數都存在新的物件中了。

 

當時我這裡存在一個疑問,每次執行n的值不是都被修改了嗎,為什麼結果都是0呢?

注意:因為你始終都是在呼叫a的方法,而你每次執行a的方法fun的時候又沒有把新返回的物件重新賦值給a,所以a裡面的fun方法被定義時所處環境中的變數n一直都是0

 

6. 當 var b = fun(0).fun(1).fun(2).fun(3); 執行的時候就和上面疑問中的情形是一樣的了。

當 fun(0)執行的時候,同上面a.fun(0)一樣,返回結果是undefined,且此時產生的新物件中,方法fun被定義時所處環境中的變數n為0

當 fun(1)執行的時候,相當於上面的a.fun(1)一樣,都是輸出0 (此時fun方法所處環境中的n為0),且返回一個新物件,新物件中的變數n為1

當fun(2)執行的時候,就不太一樣了,因為是在前一條語句執行結果後面直接呼叫fun方法, 但此時的fun方法已經不再是fun(1)中的方法了,而是上面返回的新物件中的方法,也就是變數n為1的方法,所以,這裡輸出的結果為1,且返回一個新物件,新物件中的變數n為2

當fun(3)執行的時候,和fun(2)的情況一樣,輸出結果為新的物件中的n,也就是2

 

7. 到這裡,var c = fun(0).fun(1); 應該就能夠明白為什麼這裡輸出對的結果是 undefined 和 0 了

因為道理和前面一樣,第一個 fun(0) 給n傳遞了值,但是o沒有,所以列印o的結果為undefined,第二個 fun(1) 將n的值傳給了o, 所以列印的結果為0,且這裡產生的物件被賦值給了變數c,此時方法fun被定義時的環境(也就是它的詞法作用域)中的n是被重新賦值的1 (方法fun的形參m把被傳過來的實參1,傳遞給了函式fun中的形參n)

故:c.fun(2) 和 c.fun(3)中c物件的fun方法被定義時的環境中的n都是1,所以輸出的結果也是把n的值傳遞給fun函式中的o, 即輸出1

 

此處是測試結果圖: