连接: https://segmentfault.com/q/1010000003712016
今天做一个循环想得到1,2,3...10的输出结果。自己写的放弃了,然后看到了这么一个方案,但不是很理解,不知道自己哪一方面知识的欠缺,求大神解答!!!
var funcs = [];
for (var i=0; i < 10; i++) {
funcs.push((function(value) {
return function() {
console.log(value);
}
}(i)));
}
funcs.forEach(function(func) {
func(); // outputs 0, then 1, then 2, up to 9
});
自己访问自己的变量i,在每一个循环中都传入一个i值???
知识点:
词法作用域
闭包
立即执行函数表达式
一步一步来,首先,你写的应该是这样:
for (var i=0; i < 10; i++) {
funcs.push(function(i) {
return function() {
console.log(i);
}
});
}
分析:9从哪里来?9为循环i的最终值。函数在循环结束后才调用,所以每次都是9;
但不符合你的目的(从1,2,3 ... 9)依次打印出来,问题出在它们被封闭在一个共享的全局作用域中,实际上就只有一个i,所以都是9。
还是不明白?看这里:假设循环在全局环境中,那么循环中的i(var i=0)便会被提到最上头,相对于循环中的函数, i 便是自由变量(存放在作用域链的[[Scope]]属性中),而循环中的函数都处在同一个父上下文中,它们指向了同一个[[Scope]]属性,所以 i 为同一个。。
思考:那我们给创建各自的作用域不就行了,这样就引入了立即执行函数表达式(题中为匿名函数表达式)来创建各自的作用域。所以改进代码为:
for (var i=0; i < 10; i++) {
funcs.push((function(i) {
return function() {
console.log(i);
}
}()));
}
but,结果还是一样都是9,why?因为我们创建了空白的作用域,我们要传入东西才行啊,so:
for (var i=0; i < 10; i++) {
funcs.push((function(value) {
return function() {
console.log(value);
}
}(i)));
}
这样就将 i 传进去了,就是这么任性。。。
补充1:ES6已经有let可以创建块作用域了,所以,上面可以这么写:
for (let i=0; i < 10; i++) {
funcs.push(function(i) {
return function() {
console.log(i);
}
});
}
建议你看看刚出不久的《你不知道的JavaScript上卷》,这部分讲得很透彻~
补充2:看完3楼,发现漏了i++。。i为10
首先看下失败的代码:
var funcs = []
for (var i = 0; i < 3; i++) {
funcs.push(function() {
console.log(i)
})
}
funcs[0]() // 3
funcs[1]() // 3
funcs(2)() // 3
为什么这样呢?其实如果剥离掉for
语句,以上代码会像这样子:
var funcs = [],
i = 0
funcs.push(function() {
console.log(i)
})
i++
funcs.push(function() {
console.log(i)
})
i++
funcs.push(function() {
console.log(i)
})
i++
funcs[0]() // 3
funcs[1]() // 3
funcs[2]() // 3
为什么会输出上面的结果显而易见了吧。
那么要达到所需要的要求怎么做呢?通常就在push
方法中传入一个自执行函数,
并且将i
作为自执行函数的参数,以供作用于它内部。
var funcs = [],
i = 0
funcs.push((function(i){
return function() {
console.log(i)
}
}(i)))
i++
funcs.push((function(i){
return function() {
console.log(i)
}
}(i)))
i++
funcs[0]() // 0
funcs[1]() // 1
现在还原到for
语句的形式:
var funcs = []
for (var i = 0; i < 3; i++) {
funcs.push((function(i) {
return function() {
console.log(i)
}
}(i)))
}
// 这里会依次输出 0, 1, 2
funcs.forEach(function(f){
f()
})
Over~