闭包经典问题如下:
function test() {
var arr = [];
for(var i = 0; i < 10; i++){
arr[i] = function () {
console.log(i + " ");
}
}
return arr;
}
var myArr = test();
for(var j = 0; j < 10; j++) {
myArr[j]();
}
本意是想打印1到10,却打印了10个10,为什么呢?详细解释过程如下:
test-AO: {
arr: undefined,
i: undefined
}
但此时匿名函数的作用域链为:
[[scope]]---->scopechain[0]---->test-AO;
---->scopechain[1]---->GO;
最后,将arr返回。
[[scope]] ---->scopechain[0]---->AO;
---->scopechain[1]---->test-AO;
---->scopechain[2]---->GO;
首先在自身AO寻找i,未找到,则到下一层,test-AO寻找i,但此时由于10次循环都已经执行完,test-AO中i为10,因此打印了10个10。
上面打印10个10的原因是i并没有个性化地保存在函数的作用域链中,因此,我们的目的就是将其保存在作用域链中。
采用立即执行函数来解决这个问题。先简单介绍一下立即执行函数:
(function (b) {} (a)); //外层括号将其转化为表达式,因此可以被执行
(function (b) {})(a);
// 注意:()为执行符号,只有表达式才能被执行符号执行
因此上面的例子可以改写为:
function test() {
var arr = [];
for(var i = 0; i < 10; i++) {
(function (j) {
arr[j] = function () {
console.log(j + " ");
}
} (i));
}
return arr;
}
var myArr = test();
for(var j = 0; j < 10; j++) {
myArr[j]();
}
可以看出,在原来的基础上,在arr赋值语句外层包了一个立即执行函数,这个函数的目的就是将i的值保留在function () {console.log(j + " ");}的作用域链中,我们从头来解析一下这个函数:
当i为0时,传给j,此时a[0]仍被赋值为函数体function () {console.log(j + " ");},但这个时候,该函数的作用域链为:
[[scope]] ---->scopechain[0]---->立即执行函数的AO;
---->scopechain[1]---->test-AO;
---->scopechain[2]---->GO;
此时,立即执行函数的AO为:
AO:{
j: 0
}
当i为1时,传给j,此时a[1]仍被赋值为函数体function () {console.log(j + " ");},同样,该函数的作用域链为:
[[scope]] ---->scopechain[0]---->立即执行函数的AO;
---->scopechain[1]---->test-AO;
---->scopechain[2]---->GO;
此时,立即执行函数的AO为:
AO:{
j: 1
}
注:即使执行同一个函数,每次执行均会产生不同的AO。
依此类推,可以看出,当执行数组里面的每个函数时,会到其作用域链自顶向下查找,从而可以打印出0-9.