首先,這些題目不知道在網上有沒有,反正我是被坑過了,欲哭無淚的那種.
題目是這樣的:
使用console.log方法打印變量,如果出現延遲1秒後輸出用=>表示,如果是連續輸出用,表示。
例如:1=>2,3,4=>5,表示輸出1後,延遲1秒後連續輸出2、3、4,再延遲1秒輸出5
- 下面的代碼輸出的結果是什麼?
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)
- 更改上面的代碼,使其能夠輸出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)
- 更改上面的代碼,使其能夠輸出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)
})()
最最後,我想說這真的是一次印象非常深刻的面試,上面的這些題的知識點平時在開發的過程中,其實是比較常見的,只不過比較分散,但是集中到一起可能就會矇蔽你的眼睛,影響你的判斷。 自己繼續加油吧!