作用域鏈的這種配置機制引出了一個值得注意的副作用,即閉包只能取得包含函數中任何變量的最后一個值。閉包保存的是整個變量對象,而不是某個特殊的變量。
function createFunctions() {
var result = new Array();
for (var i = 0; i < 10; i++) {
result[i] = function() {
return i;
}
}
return result;
}
上例會返回一個函數數組。表面上看,視乎每個函數都應該返回子的索引值,即位置0的函數返回0,位置1的函數返回1,以此類推。
但實際上,每個函數都返回10。因為每個函數走用于鏈中都保存著 createFunctions() 函數返回后,變量i的值是10,此時每個函數都引用著保存變量i的同一個變量對象,所以在每個函數內部i的值都是10.
但是,我們可以通過創建另一個匿名函數強制讓閉包的行為符合預期值:
function createFunctions() {
var result = new Array();
for (var i = 0; i < 10; i++) {
result[i] = function(num) {
return function() {
return num;
}(i)
}
}
return result;
}
上例中返回了一個匿名函數,且直接執行。
這樣的情況就很有趣了,該函數內部返回一個匿名函數,這個匿名函數就具有閉包的效果,返回了父級自執行函數作用域中的 num。
而父級自執行函數在被賦值前先執行了,并傳入了變量 i,所以在這個函數作用域里存儲的是每次循環傳入的變量 i,那么內部的返回的函數在執行時訪問的 num 就是 i對應的值了。
這邊有點繞,其實就是匿名函數訪問的是外部函數的變量 num,而num是不變的,且num的值是在每次循環的時候傳入的。
那么最內層的函數在執行時就不會出現前一個例子的范圍外部函數作用域的變量被改變情況。