圖解JavaScript閉包面試題
- 2019 年 10 月 3 日
- 筆記
由於最近在學習關於閉包相關的知識,並且閉包這個知識點讓我有點搞不太清楚其具體的定義,所以在網上也查閱了很多大佬的講解和對閉包的一個定義。
最後感覺還是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
此處是測試結果圖: