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

首先,這些題目不知道在網上有沒有,反正我是被坑過了,欲哭無淚的那種.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)
})()

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

相關文章
相關標籤/搜索