关键词:作用域,闭包
这个问题与for循环+setTimeout遇到的问题实际上是一样的。
问题
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
console.log(a[0](), a[4](), a[9]()); // 10, 10, 10 (*)
从(*)结果可以看出,数组a的每个元素执行后都打印10。
原因
for循环结束后,数组a每个元素都成为了匿名函数,如下所示。同时var声明的全局变量i的值也变为了10。
console.log(a);
/*
[
function () {
console.log(i);
},
function () {
console.log(i);
},
...
]
*/
console.log(i); // 10
注意:别被这个函数中的标识符
i迷惑了,它就是后面匿名函数需要用到的变量名。表示如果这个函数调用时,其所在的作用域中含i这个变量,就会打印i的值。
a[i] = function () {
console.log(i);
};
在(*)处调用a[n]()相当于执行了:
(function() {
console.log(i);
})();
因为这个函数的作用域链上只有全局作用域有变量i,即var声明的全局变量i(最终值为10),这就导致数组中每个元素执行结果都打印i的最终值10。
解决
// 方式1、let/const
for (let i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
console.log(a[0](), a[4](), a[9]()); // 0, 4, 9 (*)
// 方式2、闭包
for (var i = 0; i < 10; i++) {
a[i] = (function foo() {
return function bar() {
console.log(i);
}; // 返回的这个函数bar引用着函数foo内部作用域,并形成一个闭包
})(i);
}
console.log(a[0](), a[4](), a[9]()); // 0, 4, 9 (*)
以上内容含自己的理解,仅用于自学记录。