1. 程式人生 > >for迴圈i,最終結果是10的問題

for迴圈i,最終結果是10的問題

連線: https://segmentfault.com/q/1010000003712016

今天做一個迴圈想得到1,2,3...10的輸出結果。自己寫的放棄了,然後看到了這麼一個方案,但不是很理解,不知道自己哪一方面知識的欠缺,求大神解答!!!

var funcs = [];

for (var i=0; i < 10; i++) {
    funcs.push((function(value) {
        return function() {
            console.log(value);
        }
    }(i)));
}

funcs.forEach(function(func
)
{ func(); // outputs 0, then 1, then 2, up to 9 });

自己訪問自己的變數i,在每一個迴圈中都傳入一個i值???

知識點:

  1. 詞法作用域

  2. 閉包

  3. 立即執行函式表示式

一步一步來,首先,你寫的應該是這樣:

for (var i=0; i < 10; i++) {
    funcs.push(function(i) {
        return function() {
            console.log(i);
        }
    });
}

分析:9從哪裡來?9為迴圈i的最終值。函式在迴圈結束後才呼叫,所以每次都是9;
但不符合你的目的(從1,2,3 ... 9)依次打印出來,問題出在它們被封閉在一個共享的全域性作用域中,實際上就只有一個i,所以都是9。
還是不明白?看這裡:假設迴圈在全域性環境中,那麼迴圈中的i(var i=0

)便會被提到最上頭,相對於迴圈中的函式, i 便是自由變數(存放在作用域鏈的[[Scope]]屬性中),而迴圈中的函式都處在同一個父上下文中,它們指向了同一個[[Scope]]屬性,所以 i 為同一個。。
思考:那我們給建立各自的作用域不就行了,這樣就引入了立即執行函式表示式(題中為匿名函式表示式)來建立各自的作用域。所以改進程式碼為:

for (var i=0; i < 10; i++) {
    funcs.push((function(i) {
        return function() {
            console.log(i);
        }
    }()));
}

but,結果還是一樣都是9,why?因為我們建立了空白的作用域,我們要傳入東西才行啊,so:

for (var i=0; i < 10; i++) {
    funcs.push((function(value) {
        return function() {
            console.log(value);
        }
    }(i)));
}

這樣就將 i 傳進去了,就是這麼任性。。。
補充1:ES6已經有let可以建立塊作用域了,所以,上面可以這麼寫:

for (let i=0; i < 10; i++) {
    funcs.push(function(i) {
        return function() {
            console.log(i);
        }
    });
}

建議你看看剛出不久的《你不知道的JavaScript上卷》,這部分講得很透徹~
補充2:看完3樓,發現漏了i++。。i為10

首先看下失敗的程式碼:

var funcs = []

for (var i = 0; i < 3; i++) {
    funcs.push(function() {
        console.log(i)
    })
}

funcs[0]() // 3
funcs[1]() // 3
funcs(2)() // 3

為什麼這樣呢?其實如果剝離掉for語句,以上程式碼會像這樣子:

var funcs = [],
    i = 0
    
funcs.push(function() {
    console.log(i)
})
i++

funcs.push(function() {
    console.log(i)
})
i++

funcs.push(function() {
    console.log(i)
})
i++

funcs[0]() // 3
funcs[1]() // 3
funcs[2]() // 3

為什麼會輸出上面的結果顯而易見了吧。
那麼要達到所需要的要求怎麼做呢?通常就在push方法中傳入一個自執行函式
並且將i作為自執行函式的引數,以供作用於它內部。

var funcs = [],
    i = 0

funcs.push((function(i){
    return function() {
        console.log(i)
    }
}(i)))
i++

funcs.push((function(i){
    return function() {
        console.log(i)
    }
}(i)))
i++

funcs[0]() // 0
funcs[1]() // 1

現在還原到for語句的形式:

var funcs = []

for (var i = 0; i < 3; i++) {
    funcs.push((function(i) {
        return function() {
            console.log(i)
        }
    }(i)))
}

// 這裡會依次輸出 0, 1, 2
funcs.forEach(function(f){
    f()
})

Over~