1. 程式人生 > >閉包與作用域再次深究

閉包與作用域再次深究

最近看到一個有意思的函式

        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,這樣的寫法是不合法的。

&n