你不知道的 let
最近想梳理一下自己的知識脈絡,準備用博文記錄一下學習前端以來獲取到的各種雜七雜八的知識體系。
就先從一些微小的方面入手,然後再慢點做一下擴充套件。
複習 js 高程的時候又看到了經典的閉包,裡面有個小例子用來演示:
function createFunction() { var result = new Array() for (var i = 0; i < 10; i++) { result[i] = function() { return i } } for (var j = 0; j < result.length; j++) { result[j] = result[j]() } return result } console.log(createFunction().join(' '))
這是個很簡單的問題,初學者應該都能很輕鬆的答上來,最後打出來是:
var 的 bug
其實這是 var 的一個 bug。在 for 迴圈中,如果存在一個未在這個迴圈進行時呼叫的函式,並且這個函式內部使用了 var 定義的 i 變數,由於函式未被呼叫,裡面的 i 值我們在迴圈時是不知道的,只有在函式被呼叫的情況下這個 i 值才會被確定。
回到上面這個例子,匿名函式呼叫時打印出來的 i 全部都是 10,有理由相信,這 10 個匿名函式裡的 i 全都指向棧記憶體中的同一個值,這個值就是在 for 迴圈中 var 定義的 i,每次迴圈 i 的值都會變,最後變為 10,所以最後打印出來的也全部都是 10。
let 的不同之處及猜想
但是 let 不同,再來看一下:
function createFunction() { var result = new Array() for (let i = 0; i < 10; i++) { result[i] = function() { return i } } for (let j = 0; j < result.length; j++) { result[j] = result[j]() } return result } console.log(createFunction().join(' ')) // 0 1 2 3 4 5 6 7 8 9
打印出的是 0~9,這應該才是符合我們常識的輸出結果,那這是什麼原因呢?
看到網上很多人說這是因為 let 在塊級作用域中生效,而 var 在函式作用域中生效。但是這個原因跟上面的例子好像沒有什麼必然聯絡,因為在匿名函式被呼叫時早就脫離了上面的 for 迴圈塊。
猜想可能是在 for 迴圈中 let 定義的變數 i 在每次進入迴圈都會建立一個副本,而進入下一個迴圈之前會回收這個副本,但是由於匿名函式形成閉包,副本 i 回收不了所以最後呼叫匿名函式打印出來的值也不同,因為每個匿名函式中的 i 都是不同狀態下 i 的副本。
let 和 var 的其他不同之處就不再贅述。上述的猜想其實還是閉包引出的,沒有找到可以檢視 js 執行時棧記憶體具體 key-value 的工具,暫時證明不了副本之說,以後能證明了會補上。
這篇文章引出了閉包,這是個很大的知識點,之後幾篇再來搞定它。