天天看點

js執行問題

金三銀四搞事季,前端這個近年的熱門領域,搞事氣氛特别強烈,我朋友小偉最近就在瘋狂面試,遇到了許多有趣的面試官,有趣的面試題,我來幫這個搞事 boy 轉述一下。

以下是我一個朋友的故事,真的不是我。
for (var i = 0; i < 5; i++) {
  console.log(i);
}      

“小偉,你說說這幾行代碼會輸出什麼?”

當面試官在 Sublime 打出這幾行代碼時,我竟有點蒙蔽。蛤?這不是最簡單的一個循環嗎?是不是有陷阱啊,我思索一下,這好像和我看的那個閉包的題很像啊,這面試官是不是沒寫完啊?有毒啊。

“應該是直接輸出 0 到 4 吧...”,我弱弱的說到。

“是啊,别緊張,這題沒啥陷阱,我就是随便寫一下。”

(Excuse me?面試官你是來搞笑的嗎,吓死老子了!)

“那你在看看這幾行代碼會輸出什麼?”

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

此處補充一下:setTimeout屬于宏任務,js會先執行執行棧裡的代碼,執行完後才會執行微任務清單裡的代碼,最後才會執行宏任務隊列裡的代碼,而此時i的值已經變為4;      

額,什麼鬼,怎麼還不是我背了那麼多遍的那道閉包題,讓我想想。 setTimeout 會延遲執行,那麼執行到 console.log 的時候,其實 i 已經變成 5 了,對,就是這樣,這麼簡單怎麼可能難到老子。

“應該是開始輸出一個 5,然後每隔一秒再輸出一個 5,一共 5 個 5。”

“對,那應該怎麼改才能輸出 0 到 4 呢?”

終于到我熟悉的了,加個閉包就解決了,穩!

此處并不是閉包,而是立即執行的匿名函數。。。

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

“很好,那你能說一下,我删掉這個 i 會發生什麼嗎?”

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

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

“這樣子的話,内部其實沒有對 i 保持引用,其實會變成輸出 5。”

“很好,那我給你改一下,你看看會輸出什麼?”

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

蛤?什麼鬼,這是什麼情況,讓我想想。這裡給 setTimeout 傳遞了一個立即執行函數。額,setTimeout 可以接受函數或者字元串作為參數,那麼這裡立即執行函數是個啥呢,應該是個 undefined ,也就是說等價于:

setTimeout(undefined, ...);      

而立即執行函數會立即執行,那麼應該是立馬輸出的。

“應該是立馬輸出 0 到 4 吧。”

“哎喲,不錯哦,最後一題,你對 Promise 了解吧?”

“還可以吧...”

“OK,那你試試這道題。”

setTimeout(function() {
  console.log(1)
}, 0);
new Promise(function executor(resolve) {
  console.log(2);
  for( var i=0 ; i<10000 ; i++ ) {
    i == 9999 && resolve();
  }
  console.log(3);
}).then(function() {
  console.log(4);
});
console.log(5);      

WTF!!!!我想靜靜!

這道題應該考察我 JavaScript 的運作機制的,讓我理一下思路。

首先先碰到一個 setTimeout,于是會先設定一個定時,在定時結束後将傳遞這個函數放到任務隊列裡面,是以開始肯定不會輸出 1 。

然後是一個 Promise,裡面的函數是直接執行的,是以應該直接輸出 2 3 。

然後,Promise 的 then 應當會放到目前 tick 的最後,但是還是在目前 tick 中。

是以,應當先輸出 5,然後再輸出 4 。

最後在到下一個 tick,就是 1 。

“2 3 5 4 1”

2=>for 循環=>3=>5=>4=>1

“好滴,等待下一輪面試吧。”

So easy!媽媽再也不用擔心我的面試了。