理解闭包以后,我们来看看闭包会带来什么问题
function test() {
var arr = [];
for(var i=0; i<10; i++) {
arr[i] = function () {
document.write(i + ' ');
}
}
return arr;
}
var myArr = test();
for (var j=0; j<10; j++) {
myArr[j]();
}
在函数test( )中定义一个空数组,再通过for循环往数组中添加十个输出函数用于输出当前的索引,将数组返回给变量myArr,通过for循环执行,希望输出结果为0 1 2 3 4 5 6 7 8 9,实际输出结果却是十个10,为什么呢?
为什么是10?
在test( )执行过程中,for循环判断完i<9为true后,会继续进行i++操作。此时,i为10。再次判断i<10未false,结束循环。因为输出函数在test( )执行完毕后才被执行,这时回头找i变量,已经是10了。
为什么是十个都是10?
可能会有人有疑问,在for循环执行过程中,i的值在自增,那为什么输出结果中的i跟数组索引i不是同一个值呢?因为for循环的执行会一直改变arr[ ]的索引值,而把函数引用赋给数组arr[ ]的过程中,函数并未被执行,函数体的内容根本不会被改变。(预编译可知,函数只有在执行的时候(前一刻)才会创建自己的执行期上下文,系统都不知道未执行的函数里写的是啥)等到函数被执行时,十个输出共用的执行期上下文中的i已经是10。
简单来讲上述代码应该这样来看
function test() {
var arr = [];
for(var i=0; i<10; i++) {
arr[i] = function () {
document.write(i + ' ');//忽略函数体内容,因为未被执行
}
}
return arr;
}
var myArr = test();
for (var j=0; j<10; j++) {
myArr[j]();//在执行时再来查找函数体document.write(i + ' ');此时i已经为10
}
解决方法
function test() {
var arr = [];
for(var i=0; i<10; i++) {
(function(j) {
arr[j] = function () {
document.write(j + ' ');
}
}(i))
}
return arr;
}
var myArr = test();
for (var j=0; j<10; j++) {
myArr[j]();
}
将内层函数用立即执行函数包裹,将i作为参数传给立即执行函数。每次执行循环时,立即执行函数执行,创建AO{}对象保存参数,并将AO{}对象放在作用域链顶端(同时内层函数被创建且作用域链与立即执行函数相同),然后立即执行函数销毁,内层函数的引用被保存在数组中,形成闭包。执行十次for循环会创建十个立即执行函数。虽然最内层函数还是未被执行,但是在内层函数被执行时,内层函数的作用域链中包含着包裹自己的立即执行函数的AO{}对象。此时再通过各自的作用域链中,立即执行函数的AO{}对象查找到参数的值,输出得到想要的结果。