for (var i = 0; i < 5; i++) { setTimeout(function() { console.log( i); }, 1000); } console.log( i);
1.控制檯會輸出什麼?前端
2.若是咱們約定,用箭頭表示其先後的兩次輸出之間有 1 秒的時間間隔,而逗號表示其先後的兩次輸出之間的時間間隔能夠忽略,代碼實際運行的結果該如何描述?面試
代碼執行過程以下:bash
(1)先用循環幾乎同時設置了5個定時器;而且i的值由0變成了5;微信
(2)循環體外console.log(i),打印1個5;閉包
(3)大約1秒以後,5個定時器都觸發,打印5個5。異步
setTimeout是Js最基本的異步函數,本題主要是對Js同步和異步知識點的考察,那麼這裏說的同步和異步究竟是什麼呢?函數
"同步",一下就讓人想到"一塊兒"這個詞;"異步"呢,從字面來說,像是"一邊...一邊...",好比"小明一邊吃雪糕一邊寫做業",雪糕吃完了,做業也寫完了,看起來徹底沒毛病,可是這樣理解Js的同步和異步就錯了!Javascript語言是一門單線程的語言,全部任務能夠分紅兩種,一種是同步任務,另外一種是異步任務。同步任務指的是,在"主線程"上排隊執行的任務,只有前一個任務執行完畢,才能執行後一個任務;異步任務指的是,不進入主線程、而進入"任務隊列"的任務,只有等主線程任務執行完畢,"任務隊列"開始通知主線程,請求執行任務,該任務纔會進入主線程執行。ui
具體來講,異步執行的運行機制以下。(同步執行也是如此,由於它能夠被視爲沒有異步任務的異步執行。)spa
(1)全部同步任務都在主線程上執行,造成一個"執行棧"。線程
(2)主線程以外,還存在一個"任務隊列"。只要異步任務有了運行結果,就在"任務隊列"之中放置一個事件。
(3)一旦"執行棧"中的全部同步任務執行完畢,系統就會讀取"任務隊列",看看裏面有哪些事件。那些對應的異步任務,結束等待狀態,進入執行棧,開始執行。
(4)主線程不斷重複上面的第三步。
若是指望代碼的輸出變成:5 -> 0,1,2,3,4,該怎麼改造代碼? 熟悉閉包的小夥伴很快能給出下面的解決辦法:
for (var i = 0; i < 5; i++) {
(function(j) { // j = i
setTimeout(function() {
console.log(j);
}, 1000);
})(i);
}
console.log(i);
複製代碼
巧妙的利用 IIFE(Immediately Invoked Function Expression:聲明即執行的函數表達式)來解決閉包形成的問題,確實是不錯的思路,可是初學者可能並不以爲這樣的代碼很好懂。有沒有更符合直覺的作法?咱們只須要對循環體稍作手腳,讓負責輸出的那段代碼能拿到每次循環的i值便可。
var output = function (i) {
setTimeout(function() {
console.log(i);
}, 1000);
};
for (var i = 0; i < 5; i++) {
output(i); // 這裏傳過去的 i 值被複制了
}
console.log(i);
複製代碼
感謝您花時間讀到這裏~
按照慣例,推廣一下微信公衆號「前端麻辣燙」,主要是分享前端知識,着重前端面試知識。歡迎關注~歡迎投稿~
微信搜索微信號:WebSnacks, 或者微信掃碼關注哦!