日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

for循环中let,var 的经典面试题:for循环中 console.log(i)详解

發布時間:2024/9/27 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 for循环中let,var 的经典面试题:for循环中 console.log(i)详解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

同學們在剛準備面試時肯定見過一道經典面試題:

for(var i = 0; i < 10; i++) {setTimeOut(function(){console.log(i)}) } // 輸出 10 10 10 10 10 10 10 10 10 10for(let i = 0; i < 10; i++) {setTimeOut(function(){console.log(i)}) } // 輸出 0 1 2 3 4 5 6 7 8 9

有的同學就很疑惑,怎么肥事,這不一樣嘛!


然后就去百度查查

然后百度就會告訴你:

第一個變量i是用var聲明的,在全局范圍內有效,所以全局中只有一個變量i,每次循環時,setTimeOut定時器里指的是全局變量i,而循環里的十個setTimeOut是在循環結束后才執行,所以輸出十個10。
第二個變量i是用let聲明的,當前的i
只在本輪循環中有效,每次循環的i其實都是一個新的變量,所以setTImeOut定時器的里面的i其實不是同一變量,所以輸出0123456789

看完以后

似懂非懂

就算使用var定義的i是全局變量,每次循環都改變全局范圍里的i的值,但是循環一次,執行一次setTimeOut啊,不也應該輸出當前i值嗎?

em…

不管,面試官問我我就照著這么回答就行了,面試官肯定明白我說的什么

其實之前我理解的也是很片面的,直到這兩天看見一篇Google大佬的文章,全是英文,看了好半天****,下面👇,我就結合這篇文章給大家講講我的理解(瞎講講)吧

首先,在理解這個問題之前,我們需要理解一下macro-task(宏任務)和micro-task(微任務)

先給大家舉一個例子(大佬舉的例子):

console.log('script start');setTimeout(function() {console.log('setTimeout'); }, 0);Promise.resolve().then(function() {console.log('promise1'); }).then(function() {console.log('promise2'); })console.log('script end');

大家來仔細猜一猜執行結果是什么吶?

結果: script start script end promise1 promise2 setTimeout

嘿!有點東西

其實js是單線程的,每個線程有它自己的唯一的事件循環,但是事件循環的任務源可以不唯一。類似setTimeout, promise, ajax, DOM操作等都是典型的任務源,任務隊列中的任務便是來自這些任務源。而這些任務源產生的任務又可以分為macro-task(宏任務)和micro-task(微任務)兩種。

macro-task(宏任務)

macro-task(宏任務)中的任務都是有時間順序的,因此瀏覽器能夠有序地從中調度任務并執行。在任務與任務之間,瀏覽器可能會渲染更新。
macro-task(宏任務)中一個典型就是setTimeout,setTimeout函數等待給定的延遲事件然后將其回調函數推入宏任務Event Queue中。這就是為什么先輸出’script end’ 后輸出’setTimeout’的原因。
macro-task(宏任務)主要有:script(整體代碼), setTimeout, setInterval, setImmediate, I/O, UI rendering。

micro-task(微任務)

micro-task(微任務)中的任務在當前函數調用棧中的函數執行完成之后即調度,像promise、mutation都會被推入微任務Event Queue隊列中。并且微任務Event Queue隊列中的一個任務執行完成后,后續的micro-task(微任務)也會繼續執行,直到微任務Event Queue隊列為空,這就解釋了為什么promise2也會在setTimeout之前輸出的原因。
微任務Event Queue隊列主要有process.nextTick, Promise, Object.observe(已廢棄), MutationObserver(html5新特性)

當宏任務Event Queue隊列中的一個任務執行結束時,如果函數調用棧為空,便會開始執行微任務Event Queue隊列中的任務,直至微任務Event Queue隊列中所有任務執行完畢,然后event loop才會繼續執行宏任務Event Queue隊列中的下一個任務。

上圖文:

接下來,上個厲害的(也是大佬舉的例子)

<div class="outer"><div class="inner"></div> </div>

分別給這兩個div加上點擊事件

var outer = document.querySelector('.outer'); var inner = document.querySelector('.inner');new MutationObserver(function () {console.log('mutate'); }).observe(outer, {attributes: true, });function onClick() {console.log('click');setTimeout(function () {console.log('timeout');}, 0);Promise.resolve().then(function () {console.log('promise');});outer.setAttribute('data-random', Math.random()); }inner.addEventListener('click', onClick); outer.addEventListener('click', onClick);

大家再來仔細思考一下執行結果是什么吶?

結果: click promise mutate click promise mutate timeout timeout


嘿!有點大東西

上述例子中Dispatch click和setTimeout屬于宏任務Event Queue,對應的回調函數被推進宏任務Event Queue隊列中,Mutation observer和promise屬于微任務Event Queue,對應的回調函數則被推入微任務Event Queue隊列中。當點擊inner元素時,代碼執行執行過程如下所示

  • Dispatch click被推入宏任務Event Queue隊列中,當點擊inner元素時onClick被推入函數調用棧(Js stack)中,執行上下文進入onClick中,將setTimeout的回調函數推入宏任務Event Queue隊列中,Mutation observers和Promise then的回調函數推入微任務Event Queue隊列中,并執行輸出click。

  • 當onClick執行結束后, 函數調用棧為空,將微任務Event Queue隊列中的 promise then 的回調函數推入函數調用棧。

  • 同樣地,當promise then的回調函數執行結束后,將mutation observers的回調函數推入函數調用棧

  • 由于事件冒泡機制,父元素outer也會響應點擊事件,因此重復1-3步驟,執行結束后如下所示。

  • 此時函數調用棧和microtasks中均為空,因此event loop將執行tasks中的下一個任務。

  • 再執行tasks中的最后一個任務。

    (注:以上例子均在谷歌瀏覽器中的輸出結果)

  • 上圖文:

    把這兩個例子理解好,是不是感覺對 這道經典面試題 豁然開朗,甚至有點小菜一碟了吶、

    其實最后總結一下就是:
    當瀏覽器在執行這段

    for(var i = 0; i < 10; i++) {setTimeOut(function(){console.log(i)}) }

    代碼時
    先在全局定義變量 i, 然后執行 for 循環,執行一次 for 循環,分別將 i++ 放入函數調用棧隊列,setTimeout 放入task隊列 一次。
    因為需要將函數調用棧隊列里的任務執行結束后,再往下執行task任務
    所以 i++ 一直在執行,10次 i++ 執行結束, i 的值為10(為什么不是9?因為 i++ 在值為9時,還會進行一次i++操作,最后一個循環完 i 的值為10,不滿足條件,不再循環)。
    至此,函數調用棧隊列任務執行結束,再去執行task里的十個setTimeout任務,2而此時 i 的值為10,所以輸出10 個 10。

    最后 附上 :Google 大佬文章

    感謝大佬!雖然我們只是大佬的搬運工,但是希望在搬運之前理解好我們所搬運的內容,這已經很厲害啦。

    以上理解若有偏差,望各位同學們批評指正。

    總結

    以上是生活随笔為你收集整理的for循环中let,var 的经典面试题:for循环中 console.log(i)详解的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。