还搞不懂 for+setTimeout ?

1,183 阅读2分钟

前言

最常考的面试题!别再答不上来了,丢人丢到家了!

在面试之前,我就看到过这道题,但是没有仔细研究,到了面试时人家真的问了,我当时悔之晚矣,呜呜呜~,所以没搞懂的小伙伴快看看吧!

面试官:代码输出什么?

for (var i = 0; i < 5; i++) {
  setTimeout(function timer() {
    console.log(i)
  }, 1000)
}
// 1s后一起输出5个5console.log(i)  // 5

为啥子嘞?为什么不是5个4呀?

首先变量 i 由var声明,这个变量就是全局变量,每次循环后都覆盖原来的i。由于js的事件循环机制,先执行完for后再执行的setTimeout,所以setTimeout在1s后输出5个5

面试官:如果setTimeout中的1000变为0呢?

for (var i = 0; i < 5; i++) {
  setTimeout(function timer() {
    console.log(i)
  }, 1000)
}
// 立刻输出5个5

面试官:怎么让他输出0,1,2,3,4

方法一:立即执行函数

for (var i = 0; i < 5; i++) {
  ((j) => {
    setTimeout(() => {
      console.log(j)
    }, j * 1000)
  })(i)
}

转换成函数调用形式

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

使用立即执行函数将i传入函数内部,此时i的值固定在了j上,那么我们再执行循环的时候就变成了run(0)、run(1) ... run(4),所以最后输出0,1,2,3,4

方法二:let

for (let i = 0; i < 5; i++) {
  setTimeout(()=> {
    console.log(i)
  }, i * 1000)
}

使用let声明会触发块级作用域,所以会生成0,1,2,3,4

方法三:使用setTimeout的第三个参数

for (var i = 0; i < 5; i++) {
  setTimeout((j) => {
    console.log(j)
  },
  i * 1000,
  i)
}

没想到setTimeout还有第三个参数,我也很惊讶!那就来仔细的了解下setTimeout的参数吧!

setTimeout的三个参数

setTimeout(function, delay, arg1, ..., argN)

function 到期时间之后执行的函数

delay 延迟的毫秒数(一秒等于1000毫秒)函数的调用会在该延迟之后发生。如果省略该参数,delay取默认值0,意味着“马上”执行,或者尽快执行。不管是哪种情况,实际的延迟时间可能会比期待的(delay毫秒数) 值长,原因请查看实际延时比设定值更久的原因:最小延迟时间

arg1, ..., argN 附加参数,一旦定时器到期,他们会作为参数传递给function

结语

这道题考查差JS的基础知识:事件循环(EventLoop),函数,内存空间,执行上下文,变量对象,作用域,闭包,setTimeout定时器,真的很考验对JS基础的掌握程度。继续加油吧!