閉包與作用域再次深究
- 2019 年 10 月 3 日
- 筆記
最近看到一個有意思的函數
function test (arr) { var temp = [] for (var i =0; i<arr.length; i++) { (function() { var j = i; temp[i] = function () { return j } })() } return temp }
那麼以下的結果會列印出什麼呢?
var arr = [1,2,3,4,5] var arrFn = test(arr) console.log(arrFn[0])
結果是:0;
那麼繼續test函數換成以下兩種又會是什麼結果呢?
function test2 (arr) { var temp = [] for (var i =0; i<arr.length; i++) { temp[i] = function () { return i } } return temp }
function test3 (arr) { var temp = [] for (var i =0; i<arr.length; i++) { (function() { temp[i] = function () { return i } })() } return temp }
test2是網上比較常見的,結果是我們在讀取i的時候,i已經全部變為5;test3和test只有兩行程式碼不同,但是結果卻完全不一樣。
之前曾經理解是因為我們調用的時候for循環已經執行完,所以會拿到i為5。但是其實這只是表象,最核心的其實只有一句話:
閉包通過引用而不是值(非引用)來獲取他們外部的變數
因為是引用類型,所以在for執行完之後,i變為了5,引用類型也就繼而變為5.
在test中添加了一行可以驗證的程式碼就是:
var j = i;
通過在局部作用域聲明非引用類型,將單次循環是的引用類型保存下來,那麼就能獲取我們想要的結果了。
還有其他方法,通過自執行函數傳遞參數,將引用類型變為非引用類型都是一個原理:
function test4 (arr) { var temp = [] for (var i =0; i<arr.length; i++) { (function(j) { temp[i] = function () { return j; } })(i) } return temp }
其次就是在IIFE來創建局部作用域的時候,需要注意不能再作用域外部使用break與continue,這樣的寫法是不合法的。