记一次前端面试题中遇到的坑

首先,这些题目不知道在网上有没有,反正我是被坑过了,欲哭无泪的那种.javascript

题目是这样的:
使用console.log方法打印变量,若是出现延迟1秒后输出用=>表示,若是是连续输出用,表示。java

例如:1=>2,3,4=>5,表示输出1后,延迟1秒后连续输出二、三、4,再延迟1秒输出5es6

  1. 下面的代码输出的结果是什么?
for(var i=0;i<5;i++) {
    setTimeout(function(){
        console.log(i)
    }, 1000)
}

console.log(i)

怎么样,够简单吧,可是我特么居然答错了,真心想抽本身几下。
我给出的回答是:5=>4=>4=>4=>4=>4
下面,我说明如下我当时的脑壳里面是怎么想的:面试

for(var i=0;i<5;i++) {
    setTimeout(function(){
        console.log(i)    // 在这个地方,我认为在for中限制的条件是i<5,那么这里就会是4
    }, 1000) // 不知道为何我当时会认为这里延迟的1秒会默认*i,致使=>4=>4=>4=>4=>4
}
// 第一个5, 由于var关键字能够提高变量的做用域,出了for循环体做用域外, 其实仍是可以被访问到的,然而在for循环中最后一次对i的操做是i++,因此这里会输出5。
console.log(i)

面试官仍是很不错的,他反复提醒我再检查检查,有没有什么遗漏。因而我盯着for里面看了大概有30秒,愣是没发现问题。而后面试官很是和善可亲的微笑着跟我对了一遍for,当对到i=5的时候,个人脑壳当时就直冒冷汗,我擦!正确的答案应该是:5=>5,5,5,5,5闭包

再从新分析如下,上面的代码执行过程至关于:async

// for循环了5次,调用setTimeout方法也是5次,可是setTimeout的延迟都是1秒,
// 等setTimeout中的callback函数执行的时候,i的值已是5了,
// 因此最终1秒后会连续打印出5个5
setTimeout(function(){
    console.log(i)    // 第2个5
}, 1000)

setTimeout(function(){
    console.log(i)    // 第3个5
}, 1000)

setTimeout(function(){
    console.log(i)    // 第4个5
}, 1000)

setTimeout(function(){
    console.log(i)    // 第5个5
}, 1000)

setTimeout(function(){
    console.log(i)    // 第6个5
}, 1000)
// 这里没啥好说的
console.log(i)
  1. 更改上面的代码,使其可以输出5=>0,1,2,3,4

这个其实是考察你对闭包的掌握状况,我给出的回答是:函数

答案1:code

for(var i=0;i<5;i++) {
    setTimeout(function(j){
        console.log(j)
    }, 1000, i) // 经过setTimeout的第三个参数把i传入给callback函数的参数j
}

console.log(i) // 这里不用动

答案2:ip

for(var i=0;i<5;i++) {
    (function(j){ // j就是外部传入的参数i
        setTimeout(function(){
            console.log(j)
        }, 1000)
    })(i) // 包一层当即执行函数(IIFE),把i传入
}

console.log(i) // 这里不用动

作这道题的时候,有一个插曲,我最开始想的是把for中的var换成let就行了,面试官说:一看就知道大家用es6用的太多,我听了以后,仍是立刻反应过来了,确实不能换成let。作用域

错误的答案:

// for中的变量i用let关键字声明以后,只在for循环体内的做用域可以访问到,
// 出了这个循环体做用域,外部就访问不到了。
for(let i=0;i<5;i++) {
    setTimeout(function(){
        console.log(i)
    }, 1000)
}
// 因此,这里的i会是undefined
console.log(i)
  1. 更改上面的代码,使其可以输出0=>1=>2=>3=>4=>5

看了这个题我想了大概有2分钟,面试官说,随便你怎么改,你只要能输出这个结果就行,无奈我用了一个最最最最low的办法。

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

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

面试官看了看说,好吧这个也算你答对了,可是你能够尝试如下es六、es7的特性去完成这个(疯狂暗示),因而我分析了一下:

// 要保证从上往下的执行顺序
for(var i=0;i<5;i++) {
    // 这里的setTimeout会分别输出0=>1=>2=>3=>4
    setTimeout(function(){
        console.log(i)
    }, 1000)
}
**// 问题就是在这里,如何保证for里面的setTimeout所有执行完成以后,再执行下面的输出?**

// 这里的i输出的应该是最后一个=>5
console.log(i)

我想了大概有2分钟,决定用generator函数,可是还没写到一半的时候,面试官叫停了:其实能够用es7中的async、await配合Promise.all()来完成,不必用generator,你怎么还退到上一个版本了呢。
好吧,我感受此次面试应该是GG了。

最后我仍是贴出我用generator实现的代码

function * G() {
  for(var i=0;i<5;i++){
    yield i
  }
  return i
}
var gg = G()
do {
  const {value, done} = gg.next()
  setTimeout(() => {
    console.log(value)
  }, 1000 * value);
  if (done) {
    break;
  }
} while(true)

面试官想要的答案:

(async function () {
  var i = 0
  function a() {
    var array = []
    for(;i<5;i++){
      array[i] = new Promise((resolve) => {
        setTimeout((j) => {
          console.log(j)
          resolve()
        }, 1000 * i, i);
      })
    }
    return array
  }
  await Promise.all(a())

  console.log(i)
})()

最最后,我想说这真的是一次印象很是深入的面试,上面的这些题的知识点平时在开发的过程当中,实际上是比较常见的,只不过比较分散,可是集中到一块儿可能就会蒙蔽你的眼睛,影响你的判断。 本身继续加油吧!

相关文章
相关标签/搜索