这个问题在这里已有答案:
此代码记录6
,6次:
(function timer() {
for (var i=0; i<=5; i++) {
setTimeout(function clog() {console.log(i)}, i*1000);
}
})();
但是这段代码......
(function timer() {
for (let i=0; i<=5; i++) {
setTimeout(function clog() {console.log(i)}, i*1000);
}
})();
...记录以下结果:
0
1
2
3
4
5
为什么?
是因为let
以不同的方式绑定每个项目的内部范围,var
保持i
的最新值?
使用var
,您有一个函数范围,并且只有一个共享绑定用于所有循环迭代 - 即每个setTimeout回调中的i
意味着在循环迭代结束后最终等于6的相同变量。
使用let
,你有一个块范围,当在for
循环中使用时,你会得到每个迭代的新绑定 - 即每个setTimeout回调中的i
意味着一个不同的变量,每个变量都有不同的值:第一个是0,下一个是1等
所以这:
(function timer() {
for (let i = 0; i <= 5; i++) {
setTimeout(function clog() { console.log(i); }, i * 1000);
}
})();
相当于仅使用var:
(function timer() {
for (var j = 0; j <= 5; j++) {
(function () {
var i = j;
setTimeout(function clog() { console.log(i); }, i * 1000);
}());
}
})();
使用立即调用的函数表达式来使用函数作用域,其方式类似于块作用域在let
示例中的作用。
如果不使用j
名称,它可以写得更短,但也许它不会那么清楚:
(function timer() {
for (var i = 0; i <= 5; i++) {
(function (i) {
setTimeout(function clog() { console.log(i); }, i * 1000);
}(i));
}
})();
使用箭头功能更短:
(() => {
for (var i = 0; i <= 5; i++) {
(i => setTimeout(() => console.log(i), i * 1000))(i);
}
})();
(但如果你可以使用箭头功能,没有理由使用var
。)
这就是Babel.js如何使用let
将您的示例转换为在let
不可用的环境中运行:
"use strict";
(function timer() {
var _loop = function (i) {
setTimeout(function clog() {
console.log(i);
}, i * 1000);
};
for (var i = 0; i <= 5; i++) {
_loop(i);
}
})();
感谢Michael Geary在评论中发布了Babel.js的链接。请参阅评论中的链接以获取实时演示,您可以在其中更改代码中的任何内容并观看立即进行的翻译。看看其他ES6功能如何被翻译也很有趣。
从技术上讲,这就是@rsp在他出色的答案中所解释的。这就是我喜欢理解引擎下工作的方式。对于使用var
的第一个代码块
(function timer() {
for (var i=0; i<=5; i++) {
setTimeout(function clog() {console.log(i)}, i*1000);
}
})();
你可以想象编译器在for循环中是这样的
setTimeout(function clog() {console.log(i)}, i*1000); // first iteration, remember to call clog with value i after 1 sec
setTimeout(function clog() {console.log(i)}, i*1000); // second iteration, remember to call clog with value i after 2 sec
setTimeout(function clog() {console.log(i)}, i*1000); // third iteration, remember to call clog with value i after 3 sec
等等
由于使用i
声明var
,当调用clog
时,编译器在最近的功能块中找到变量i
,这是timer
,因为我们已经到达for
循环的末尾,所以i
保持值6,并执行clog
。这解释了6次被记录六次。