一个js面试题

for (var i = 0; i < 5; i++) {
    setTimeout(function() {
        console.log(new Date, i);
    }, 1000);
}

console.log(new Date, i);

能想到答案是5,5,5,5,5。感觉应该是因为异步代码。但根据作者的话“ 只要你对 JS 中同步和异步代码的区别、变量作用域、闭包等概念有正确的理解,就知道正确答案”。不知道这里怎么和闭包有关系。
还有个就是输出的结果为什么最后4个5是同时输出的。
2017-03-18T00:43:45.873Z 5
2017-03-18T00:43:46.866Z 5
2017-03-18T00:43:46.868Z 5
2017-03-18T00:43:46.868Z 5
2017-03-18T00:43:46.868Z 5
2017-03-18T00:43:46.868Z 5

阅读 3.1k
4 个回答

setTimeout是延时执行,但是他其实是不准确的。后面的毫秒数代表着他在多少毫秒后会进入执行队列里面。如果你前面有一个占用了非常多时间的运算。setTimeout就会延时特别久。他只在当前队列执行完之后,才尝试执行。

for (var i = 0; i < 5; i++) {
    //延迟执行
    setTimeout(function() {
        console.log(new Date, i);
    }, 1000);
}    
console.log(new Date, i);
alert("我们用alert挂起一下程序,你等待两三秒再确定");
//执行到这里,这个队列才算是执行完了。然后才会尝试执行你的settimeout延时的程序,看看是不是可以调用了。不信你看看时间

clipboard.png

闭包的产生就是局部作用域中有对外部作用域变量的引用,原代码指向的是一个全局变量所以毫无意义

for (let i = 0; i < 5; i++) {
    setTimeout(function() {
        console.log(new Date, i);
    }, 1000);
}

console.log(new Date, i);

这样就很好理解了,let声明的变量具有块级作用域,只有在代码块中能使用,代码块中挂起的异步函数能访问到i(块级作用域中的i)
异步函数中有对外部变量i的引用从而产生闭包
而外部使用变量会因为没有声明而报错
一开始的代码输出5个5是因为执行异步代码 setTimeout(function() { console.log(new Date, i); }, 100);中的console.log(new Date, i);时for循环已经完毕了,i已经是5了,注意setTimeout(...)是同步的,其中的代码块是异步的
或者这样

for (var i = 0; i < 5; i++) {
    (function(i){
        setTimeout(function() {
            console.log(new Date, i);
        }, 1000);
    })(i);
}

console.log(new Date, i);

这里相当于保存了i的快照版本作为参数传入
至于为什么是同步输出的(其实是依次输出的),for循环执行setTimeout是同步的,又没阻塞代码存在每次循环的时间间隔小到可以忽略不计,执行异步代码也没有其他异步代码与阻塞代码的干扰,所以执行的时候感觉是一瞬间一起出现的

  • 异步
    setTimeout设定的函数处理器后,相当于开启一个异步进程去执行函数处理器的内容,在1秒延迟过程中,for循环执行完毕,i值变成5,此时输出就是5 5 5 5 5

  • 闭包
    只要是一个函数引用了外部作用域中的变量,使得变量的生命周期得到延长,就是闭包

  • 同时
    并不是,只是因为执行较快,改成如下,可以看得到最终输出的5个结果并不完全一致

for (var i = 0; i < 5; i++) {
    setTimeout(function() {
        console.log(new Date().getTime(), i);
    }, 1000);
}

1 执行for循环 把五个定时器加入队列 同时全局作用域的 i 修改为 5;
2 执行console.log(new Date, i); //2017-03-18T00:43:45.873Z 5;
3 1000ms后五个定时器顺序执行console.log(new Date, i) 速度非常快所以看起来就是同时输出五个

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题