js 中 for 循环的语法糖解开是什么样的?为什么 let 和 var 导致输出结果不同?

2020-12-27 23:18:16 +08:00
 Newyorkcity
function foo() {
    var funcs = [];
    for (var i = 0; i < 3; i++) { //此处使用 var i = 0
        funcs[i] = function () {
            console.log("funcs - " + i);
        };
    }
    for (var j = 0; j < 3; j++) {
        funcs[j]();
    }
}
foo();

输出结果:
funcs - 3
funcs - 3
funcs - 3

function foo() {
    var funcs = [];
    for (let i = 0; i < 3; i++) { //此处使用 let i = 0
        funcs[i] = function () {
            console.log("funcs - " + i);
        };
    }
    for (var j = 0; j < 3; j++) {
        funcs[j]();
    }
}
foo();

输出结果:
funcs - 0
funcs - 1
funcs - 2

我知道 let 的作用域被 {} 限制,而 var 的作用域等于其所在的函数的函数体,但就从这个知识点我不能解释上述情况出现的原因。我觉得其中我对 for 语法糖背后的实际代码不了解也是重要的原因,所以来请教下。感谢。

2626 次点击
所在节点    问与答
30 条回复
3wdddd
2020-12-28 10:31:05 +08:00
@yaphets666 这样很慢,应该用 promise.all
3wdddd
2020-12-28 10:32:24 +08:00
i 在整个循环内有多个副本,var 只有函数顶部一个
zankard
2020-12-28 10:44:07 +08:00
可以去了解下 js 的 Lexical Environment 。let 是 block scope,每一次 for 迭代都会生成一个新的 Lexical Environment,使得每个迭代使用的 let 变量都不一样;而 var 是 function scope,使用的都是一个变量。
AmoreLee
2020-12-28 11:00:03 +08:00
kx5d62Jn1J9MjoXP
2020-12-28 11:10:20 +08:00
var 不支持 local scope, for 循环定义的 var 是定义在函数体中的
zckevin
2020-12-28 13:03:19 +08:00
// {
// let/const x = i;
// temp_x = x;
// first = 1;
// undefined;
// outer: for (;;) {
// let/const x = temp_x;
// {{ if (first == 1) {
// first = 0;
// } else {
// next;
// }
// flag = 1;
// if (!cond) break;
// }}
// labels: for (; flag == 1; flag = 0, temp_x = x) {
// body
// }
// {{ if (flag == 1) // Body used break.
// break;
// }}
// }
// }
zckevin
2020-12-28 15:20:00 +08:00
《 let/const for loop 的 v8 实现》
https://zhuanlan.zhihu.com/p/340068236
Arthur2e5
2020-12-29 17:15:03 +08:00
@autoxbc 没有人用 for ? for 的三段形式是对过程式程序循环的最基本表达,有设置循环变量、循环条件、向下一个循环准备的内容。

最基本的 (i = 0; i < ...; i++) 当然可以用 for of 的 iterator 表达(说 for in 的建议吊起来打),但并不是所有循环都是只有一种 iterable 提供的循环方法的。即使是最基本的 Array 都有倒过来迭代的理由,换到地铁图的图论玩意儿那当然更有。

是,你可以自己写别的的 iterator 把 [Symbol.iterator] 换了。但是你自己看看那玩意是不是就等于把那三段东西又写了一遍,还得复制粘贴空白模板代码?
autoxbc
2020-12-29 20:51:24 +08:00
@Arthur2e5 #28
for 里的循环变量显示 for 的设计者认为下标访问是对迭代的正确抽象,事实是对迭代的正确抽象是迭代器。迭代器可以设计成正向迭代,自然也可以添加反向迭代方法,这并没有超出设计范围

ES6 对所有集合体对象部署了迭代器,没有对任何集合体添加新的下标访问接口,原有的基于下标的迭代过程实现为基于迭代器接口,这就是工业界给出的答案
Arthur2e5
2020-12-30 00:12:04 +08:00
> @autoxbc 下标访问是对迭代的正确抽象

谁说循环变量是下标了?

> 正确抽象是迭代器

你再好好想想,迭代器储存了什么状态?迭代器储存的状态是不是一种循环变量?

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/739474

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX