此代码记录css()
,共6次:
input.login-error {
border: 3px solid red;
}
p.login-error {
color: red;
}
但是这段代码......
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
的最新值?
答案 0 :(得分:37)
使用 var
,您有一个函数范围,并且只有一个共享绑定用于所有循环迭代 - 即每个setTimeout回调中的i
表示循环迭代结束后 finally 等于6的相同变量。
使用 let
,您有一个块范围,当在for
循环中使用时,您会为每次迭代获得一个新的绑定 - 即每个迭代中的i
setTimeout回调意味着一个不同的变量,每个变量都有不同的值:第一个为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功能如何被翻译也很有趣。
答案 1 :(得分:5)
从技术上来说,这就是@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次被记录六次。