闭包中循环部分的理解

Posted by Lxxyx on 2016-01-08

第一个例子

1
2
3
4
5
6
7
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 10

这是一段很经典的代码。之前一直无法理解,为什么console.log(i)的时候,i是i而不是a[i]所对应的值。

看似数组a中,每个元素都取到了相应的i

但是。数组a其实取到的是那个函数,i的话,其实是在运行的时候读取的。

如果运行a[6]();函数,那么实质上i从全局作用域中读取,此时的i已经是循环结束后的那个10了。

对此,只需稍作改造。

1
2
3
4
5
6
7
8
9
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = (function(num) {
return function() {
console.log(num)
}
})(i)
}
a[6]();

我们把

1
2
3
a[i] = function () {
console.log(i);
};

改成了

1
2
3
4
5
a[i] = (function(num) {
return function() {
console.log(num)
}
})(i)

此时,循环中的i作为num的值传递给匿名立即执行函数,函数运行后返回一个函数,保持着对传进来的num的引用。十个函数便有十个闭包。

此时a[i]又等于函数。运行a[i]时,就会读取闭包所保存的变量。

第二个例子

1
2
3
4
5
6
7
8
9
10
/*
* 本意是想,每一秒输出一个i。
* 因为setTimeout的时间定位i*1000,
* 也就是1,2,3……秒时各执行一次
*/
for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, i * 1000);
}

本意是想,每一秒输出一个i。但却会输出5个5。

原因和上面的一样,for循环在一瞬间执行完毕。console.log(i);中的i,则会沿着原型链向上查找。

此时全局变量中的i,已经是for循环结束后的i了。

自己的解决方法

1
2
3
4
5
6
7
8
9
for (var i = 0; i < 5; i++) {
(function(num) {
return (function() {
setTimeout(function() {
console.log(num);
}, num * 1000);
})()
})(i)
}

这是一开始自己写的解决方法。。虽然能用,但是一大坨看起来很不爽。于是看了看别人写的,顿时那个汗颜啊。

1
2
3
4
5
6
7
for (var i = 0; i < 5; i++) {
(function(num) {
setTimeout(function() {
console.log(num)
}, num * 1000)
})(i)
}

自己的错误在于return了一个IIFE函数。事实上是没有必要的。循环中直接执行就行。

感想

到这儿的话,总算对闭包有所了解了。

包括之前迷迷糊糊的this值的四种状态,闭包会导致内存泄漏等问题。总算是了解了个清楚。下回写博客时候,给一起写出来。

let的基本用法,也是总算对闭包有所了解的地方