for循环i,最终结果是10的问题

连接: 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值???


知识点:

  1. 词法作用域

  2. 闭包

  3. 立即执行函数表达式

一步一步来,首先,你写的应该是这样:

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~


你可能感兴趣的:(for循环i,最终结果是10的问题)