for (var i=1; i<=5; i++) 加 setTimeout 如何输出1、2、3 、4、5
大家在笔试或者面试的时候,或多或少都做过这样的题吧!
请问下面代码的输出是什么?
for (var i=1; i<=5; i++) {
setTimeout( function timer(){
console.log( i );
}, i*1000 );
}
//输出 6 6 6 6 6
对于这段代码我们一般期待的是分别打印数字1、2、3 、4、5 一秒一个,然而实际上,它会打印5个6 ,同样也是一秒打印一个。
???为什么打印的不是1、2、3 、4、5 ?为什么打印的是**5个6 **不是5个5?
为什么打印的是5个6 不是5个5?
首先,来解释一下为什么会打印5个6,因为for循环的结束条件是 i<=5 ,所以当循环满足结束条件时 i 是6。所以,输出的结果5个6。
没理解的,可以按照for循环的执行顺序对照一下;
for循环执行顺序如下:
for(条件①;条件②;条件③){
循环体④
}
①②④③ ②④③ ②④③… …直到循环结束
为什么打印的不是1、2、3 、4、5 ?
这是因为在迭代期间,循环的每次迭代都对 i 拷贝。
这5个timer() 在每次迭代中分离定义i ,但是,这些闭包i 都在同一作用域上,而它事实上是一个 i 。所以,所有timer() 都输出相同的 i 。
迭代中分离定义i 没明白?
看完下面这段等价代码你就明白了
var i=1;
setTimeout( function timer(){
console.log( i );
}, i*1000 );
var i=2;
setTimeout( function timer(){
console.log( i );
}, i*1000 );
var i=3;
setTimeout( function timer(){
console.log( i );
}, i*1000 );
var i=4;
setTimeout( function timer(){
console.log( i );
}, i*1000 );
var i=5;
setTimeout( function timer(){
console.log( i );
}, i*1000 );
var i=6; //i<=5 结束循环
//setTimeout( function timer(){
// console.log( i );
//}, i*1000 );
//输出 6 6 6 6 6
好了,这回知道了为什么打印的不是1、2、3 、4、5 了,因为这些闭包绑定的i 都在同一个作用域,后面的i 把前面的i 给覆盖了。
知道了问题出在哪里就好办了。只要闭包绑定当前作用域的i ,就可以输出1、2、3 、4、5 。
那么,怎么可以创建作用域呢!
创建作用域
可以用ES6的let 定义迭代变量实现,因为用let 定义的变量创建作用域。
for (let i=1; i<=5; i++) {
setTimeout( function timer(){
console.log( i );
}, i*1000 );
}
//输出 1 2 3 4 5
虽然用let 输出了1、2、3 、4、5 ,但这很明显不符合标题的‘用for (var i=1; i<=5; i++) 输出1、2、3 、4、5’;
那还可以用什么方法在for循环中创建作用域呢!
方法一、 立即执行函数 – IIFE
在for循环中,通过给IIFE传参,实现输出1、2、3 、4、5 ;
for (var i=1; i<=5; i++) {
(function(i){
setTimeout( function timer(){
console.log( i );
}, i*1000 );
})( i );
}
//输出 1 2 3 4 5
方法二、 catch
在for循环中,利用 try/catch 强制抛出一个错误i ,然后在 catch() 子句中接收它,实现输出1、2、3 、4、5 ;
for (var i=1; i<=5; i++) {
try {
throw i;
} catch (i) {
console.log( i );
}
}
到此就回答了这个问题;
如果大家还有什么其他的想法,欢迎评论区交流!
|