記一次前端面試題中遇到的坑

首先,這些題目不知道在網上有沒有,反正我是被坑過了,欲哭無淚的那種.

題目是這樣的:
使用console.log方法打印變量,如果出現延遲1秒後輸出用=>表示,如果是連續輸出用,表示。

例如:1=>2,3,4=>5,表示輸出1後,延遲1秒後連續輸出2、3、4,再延遲1秒輸出5

  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

再重新分析以下,上面的代碼執行過程相當於:

// 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:

for(var i=0;i<5;i++) {
    setTimeout(function(j){
        console.log(j)
    }, 1000, i) // 通過setTimeout的第三個參數把i傳入給callback函數的參數j
}

console.log(i) // 這裏不用動

答案2:

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)

面試官看了看說,好吧這個也算你答對了,但是你可以嘗試以下es6、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)
})()

最最後,我想說這真的是一次印象非常深刻的面試,上面的這些題的知識點平時在開發的過程中,其實是比較常見的,只不過比較分散,但是集中到一起可能就會矇蔽你的眼睛,影響你的判斷。 自己繼續加油吧!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章