如何用JavaScript的for循环,原生和ES6写法|按秒依次输出指定数字

这个问题偶然在群里看见,就把大佬的回答记录下来了。

大佬写了两个一个是旧js的,一个是es6的。

这个是旧版JS的写法,为了构建一个隔离的作用域,防止setTimeout内部访问到全局的i变量,使用了iife函数,增加一个作用域(旧版JS只有函数可以独立作用域)。这样每次压入栈的setTimeout内部使用的i就是实时变化后的i,而不是最后的i。作用域从内层向上层寻找。

1
2
3
4
5
6
7
8
9
10
11
(function(){
for (var i = 0; i < 3; i++) {

(function(ii){
setTimeout(
function(){
console.log(ii);
},1000*ii);
})(i)
}
})();

null


现在我们试试新的let:现在执行是依次输出 0 1 2 按照1s的顺序。

按照顺序入栈,由于函数提升,内部代码会被解析,但是未执行。

也就是说,内部i三次都会传入当前I的值,和上面的例子不同的是,let 声明的i是局部的,每次执行都是一个新的值!

上面的例子存在引用,当最后i变为3的时候,再去执行定时器内部的console引用的是全局变量i所以都是3,而现在这个例子,i不是全局的,当最后入栈的定时器执行时,i依旧是当时传入的i,第一次是0,第二次是1。

这里一定注意:两个例子都是传入了当时的i(0,1,2)。

但是最终执行的时候:

var 的i 是最终变化的为准(存在引用,传入1个旧的i)。

let 的i是每次传入新的i,不是引用先前的,所以,下面的例子输出是传入新的的i(传入3个新的i)。

如果下面代码给成 var 的,会输出3次3,因为函数提升后,最终执行的i是全局变量i,而i已经被改变了。

1
2
3
4
5
6
7
8
9
10
11

(function(){
for (let i = 0; i < 3; i++) {
console.log("out:" + i);
setTimeout(
function(){
console.log(i);
},1000*i);

}
})();

null

然后想起来setTimeout有第三个参数,就加上一个:其实目的就一个,让内层的参数与外层分离开。

这个写法也是旧版的,和第一个其实一样的,只是利用了函数的作用域。每次函数入栈,传入i,当开始执行时候,i就不是最外层了,因为内层有一个作用域。就近。

1
2
3
4
5
6
7
8
(function(){
for (var i = 0; i < 3; i++) {
setTimeout(
function(ii){
console.log(ii);
},1000*i,i);
}
})();

null