先看一段代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| var a = []; var i = 0; for(; i < 2; i ++) { a[i] = function() { console.log(i); } } a.forEach(function(fn, i) { fn(); }) 结果: 2 2
|
可能很多人对这段代码无法理解,程序的设计者设计的初衷是想给数组a放置打印下标的function, 可实际执行结果是,数组a里存放的function打印的不是对应的下标,而全都是2!?!? 无法理解? 不要急,等我一步一步的调试下,可能会对你有所帮助呢
1,下面这段代码相信都没有疑问
1 2 3 4 5 6 7 8
| var i = 0; var test = function() { console.log(i); } test(); 结果: 0
|
2,这一步
1 2 3 4 5 6 7 8 9
| var i = 0; var test = function() { console.log(i); } i++; test(); 结果: 1
|
test声明的时候, 此时i=0,但是调用的时候,i已经变成了1
3,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| var i = 0; var test = function() { console.log(i); } i++; var test2 = function() { console.log(i); } i++; test(); test2(); 结果: 2 2
|
再加一个函数test2, 同时再次i++, 函数内容同样是打印i, 结果是两次打印都是2, 因为这两个函数中的i是同一个值,并且打印时i的值不是声明时的值,而是和调用时的值保持一致,
4,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| var a = []; var i = 0; a.push(function() { console.log(i); }); i++; a.push(function() { console.log(i); }); i++; a[0](); a[1](); 结果: 2 2
|
一样的, 只是把函数存进了数组
5, 然后还是一样的, 只是放进了循环中,但现在, 已经无缝过渡到了开头的那一段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| var a = []; var i = 0; for(; i < 2; i ++) { a[i] = function() { console.log(i); } } a.forEach(function(fn, i) { fn(); }) 结果: 2 2
|
6, 怎么让程序按我们期望的方式输出呢? 一个解决办法是, 将函数嵌套在一个匿名函数里,通过匿名函数来传i值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| var a = []; var i = 0; for(; i < 2; i ++) { a[i] = (function(v) { return function() { console.log(v); } })(i); } a.forEach(function(fn, i) { fn(); }) 结果: 0 1
|
原因是,通过匿名函数传值后,匿名函数里的v是局部变量,是函数中非公用的活动对象,不会受外面i的影响,因此能够将我们期望的下标值打印出来
结论: 其实这就是一个闭包引起的副作用(该闭包没有外部函数,因为外部函数不是必需的(是不是可以暂称此类闭包为开包openure)), 当前函数的活动对象的活动对象其实就是全局变量对象, 因为没有外部函数, 这个数组里的所有函数共用一个活动对象, 包括a和i, 所有这里的函数共用一个i, 所以每个函数里的i值都是最后一个值2
欢迎指教和探讨!