摘要:說運(yùn)行機(jī)制之前,先看一段代碼如果你看到這段代碼,并知道正確的輸出順序。執(zhí)行棧執(zhí)行棧也就是常說的調(diào)用棧,它是一種擁有后進(jìn)先出的數(shù)據(jù)結(jié)構(gòu)。事件循環(huán)是單線程的,也就是說它一次僅能處理一個(gè)任務(wù)。好了,最后的結(jié)果就是。
說javascript運(yùn)行機(jī)制之前,先看一段代碼:
console.log(1) Promise.resolve().then(function () { console.log(2) }) new Promise(function(resolve, reject){ console.log(3) resolve() }).then(function () { console.log(4) setTimeout(function () { console.log(5) }) }) console.log(6) setTimeout(function () { Promise.resolve().then(function () { console.log(7) setTimeout(function () { console.log(8) }) }) })
如果你看到這段代碼,并知道正確的輸出順序。那說明你對(duì)這塊掌握的差不多了。(直接翻到最后看結(jié)果)
好,言歸正傳。
在說是怎么運(yùn)行的之前,先看幾個(gè)概念。
執(zhí)行上下文(Execution Context)執(zhí)行上下文簡(jiǎn)單來說就是一個(gè)執(zhí)行環(huán)境。它有全局環(huán)境、函數(shù)環(huán)境和eval函數(shù)環(huán)境之分。它會(huì)在javascript引擎執(zhí)行你的腳本的時(shí)候去創(chuàng)建。
執(zhí)行棧(Execution Stack)執(zhí)行棧也就是常說的調(diào)用棧,它是一種擁有LIFO(后進(jìn)先出)的數(shù)據(jù)結(jié)構(gòu)。它會(huì)存儲(chǔ)代碼運(yùn)行時(shí)創(chuàng)建的執(zhí)行上下文
微任務(wù)(micro task)與宏任務(wù)(macro task)javasript中的任務(wù)分為微任務(wù)和宏任務(wù)兩種,這兩種任務(wù)的執(zhí)行時(shí)機(jī)是不同的,因此區(qū)分js中哪些是宏任務(wù),哪些是微任務(wù)則十分重要。我們常見的宏任務(wù)有:script任務(wù)、setTimeout、ajax等,常見的微任務(wù)比較典型的是:Promise.resolve().then()、process.nextTick、MutationObserver等。
事件循環(huán)(event loop)js是單線程的,也就是說它一次僅能處理一個(gè)任務(wù)。但js所在的宿主環(huán)境,也就是我們所說的瀏覽器并不是單線程的(這里宿主環(huán)境僅討論瀏覽器)。它在遇到一些任務(wù)時(shí),比如說setTimeout、event listener等。它會(huì)告訴瀏覽器:老兄幫個(gè)忙,事成后通知我一聲,小弟我先干別的事去了。瀏覽器會(huì)回應(yīng)說:交給我吧,小老弟,事成后我放到任務(wù)隊(duì)列,自己去取啊。于是,js開始執(zhí)行script任務(wù),執(zhí)行完了就開始檢查有沒有微任務(wù)啊,沒有的話就從任務(wù)隊(duì)列開始取宏任務(wù)執(zhí)行,每執(zhí)行完一次宏任務(wù),就去看看有沒有微任務(wù),有的話就執(zhí)行完成,再執(zhí)行宏任務(wù),如此往復(fù)。如下:
了解了這幾個(gè)概念,再來看看javascript是怎么執(zhí)行代碼的就比較輕松愉快了。開始吧
console.log(1) Promise.resolve().then(function () { console.log(2) }) new Promise(function(resolve, reject){ console.log(3) resolve() }).then(function () { console.log(4) setTimeout(function () { console.log(5) }) }) console.log(6) setTimeout(function () { Promise.resolve().then(function () { console.log(7) setTimeout(function () { console.log(8) }) }) })
js引擎在執(zhí)行這段代碼的時(shí)候,首先將全局執(zhí)行上下文壓入棧中:
然后呢,在執(zhí)行的時(shí)候會(huì)碰到console.log函數(shù),將它壓入棧中:
這個(gè)時(shí)候,直接執(zhí)行console函數(shù),并輸出1。然后console函數(shù)出棧:
繼續(xù)往下執(zhí)行,碰到了Promise.resolve().then(),先將Promise.resolve().then()壓入棧中(這里,我為了圖方便就把它看成整體了,不然得畫很多圖)。
然后執(zhí)行Promise.resolve().then(),前面說過,這個(gè)then()函數(shù)是個(gè)微任務(wù),它會(huì)將傳入給它的回調(diào)函數(shù)加入到微任務(wù)隊(duì)列中。
然后Promise.resolve().then()就出棧了。
接著執(zhí)行,遇到promise的構(gòu)造函數(shù),這個(gè)構(gòu)造函數(shù)是一個(gè)宏任務(wù),會(huì)直接將傳遞給它的函數(shù)壓入棧中。
執(zhí)行console函數(shù)并輸出3,執(zhí)行完,console函數(shù)出棧,接著執(zhí)行resolve()函數(shù),并出棧。
然后繼續(xù)執(zhí)行then函數(shù),將傳遞給then函數(shù)的參數(shù)函數(shù)放到微任務(wù)隊(duì)列中:
繼續(xù)來,繼續(xù)往下執(zhí)行。碰到console.log(6),二話不說,直接壓入棧中,執(zhí)行,輸出6,出棧,一氣呵成。
接著,引擎碰到了setTimeout函數(shù),這家伙是個(gè)宏任務(wù),但同時(shí)它會(huì)將傳遞給它的函數(shù),加入到任務(wù)隊(duì)列中:
好了,到此第一波宏任務(wù)就全部執(zhí)行完畢。接著,引擎就會(huì)去看一下微任務(wù)隊(duì)列中有沒有任務(wù),如果有的話,執(zhí)行它們。
現(xiàn)在看到的是,微任務(wù)隊(duì)列中有兩個(gè)任務(wù)。按照隊(duì)列的先入先出規(guī)則,先從function () {console.log(2)}開始執(zhí)行。先是函數(shù)入棧,然后執(zhí)行函數(shù),輸出2,然后函數(shù)出棧。
接著執(zhí)行下面這段代碼:
console.log(4) setTimeout(function () { console.log(5) })
先從console.log(4)開始,先將它入棧,然后執(zhí)行它,輸出4,然后函數(shù)出棧。
接著執(zhí)行:
setTimeout(function () { console.log(5) })
將
function () { console.log(5) }
加入到任務(wù)隊(duì)列中去
先執(zhí)行:
function(){ Promise.resolve().then(function () { console.log(7) setTimeout(function () { console.log(8) }) }) }
這里執(zhí)行這個(gè)函數(shù)的時(shí)候遇到一個(gè)微任務(wù),將這個(gè)微任務(wù)添加到微任務(wù)隊(duì)列,如下:
這批次的宏任務(wù)就執(zhí)行完畢了,接著就回去檢查微任務(wù)隊(duì)列中有沒有待執(zhí)行的任務(wù)。一看還真有兩個(gè)小可愛等待執(zhí)行,于是沒什么好說的,直接擰出去就執(zhí)行
先是執(zhí)行console.log(7),然后輸出7。接著執(zhí)行setTimeout,將傳遞給他的任務(wù)添加到任務(wù)隊(duì)列中去:
最后就剩這兩個(gè)函數(shù)了,按照隊(duì)列的先入后出一次執(zhí)行吧,輸出5和8。
好了,最后的結(jié)果就是1,3,6,2,4,7,5,8。你寫對(duì)了了嗎?
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/101033.html
摘要:關(guān)鍵字計(jì)算為當(dāng)前執(zhí)行上下文的屬性的值。毫無疑問它將指向了這個(gè)前置的對(duì)象。構(gòu)造函數(shù)也是同理。嚴(yán)格模式無論調(diào)用位置,只取顯式給定的上下文綁定的,通過方法傳入的第一參數(shù),否則是。其實(shí)并不屬于特殊規(guī)則,是由于各種事件監(jiān)聽定義方式本身造成的。 this 是 JavaScript 中非常重要且使用最廣的一個(gè)關(guān)鍵字,它的值指向了一個(gè)對(duì)象的引用。這個(gè)引用的結(jié)果非常容易引起開發(fā)者的誤判,所以必須對(duì)這個(gè)關(guān)...
摘要:巧前端基礎(chǔ)進(jìn)階全方位解讀前端掘金我們?cè)趯W(xué)習(xí)的過程中,由于對(duì)一些概念理解得不是很清楚,但是又想要通過一些方式把它記下來,于是就很容易草率的給這些概念定下一些方便自己記憶的有偏差的結(jié)論。 計(jì)算機(jī)程序的思維邏輯 (83) - 并發(fā)總結(jié) - 掘金從65節(jié)到82節(jié),我們用了18篇文章討論并發(fā),本節(jié)進(jìn)行簡(jiǎn)要總結(jié)。 多線程開發(fā)有兩個(gè)核心問題,一個(gè)是競(jìng)爭(zhēng),另一個(gè)是協(xié)作。競(jìng)爭(zhēng)會(huì)出現(xiàn)線程安全問題,所以,本...
摘要:巧前端基礎(chǔ)進(jìn)階全方位解讀前端掘金我們?cè)趯W(xué)習(xí)的過程中,由于對(duì)一些概念理解得不是很清楚,但是又想要通過一些方式把它記下來,于是就很容易草率的給這些概念定下一些方便自己記憶的有偏差的結(jié)論。 計(jì)算機(jī)程序的思維邏輯 (83) - 并發(fā)總結(jié) - 掘金從65節(jié)到82節(jié),我們用了18篇文章討論并發(fā),本節(jié)進(jìn)行簡(jiǎn)要總結(jié)。 多線程開發(fā)有兩個(gè)核心問題,一個(gè)是競(jìng)爭(zhēng),另一個(gè)是協(xié)作。競(jìng)爭(zhēng)會(huì)出現(xiàn)線程安全問題,所以,本...
摘要:毫無疑問,設(shè)計(jì)模式于己于他人于系統(tǒng)都是多贏的設(shè)計(jì)模式使代碼編寫真正工程化設(shè)計(jì)模小書前端掘金這是一本關(guān)于的小書。 JavaScript 常見設(shè)計(jì)模式解析 - 掘金設(shè)計(jì)模式(Design pattern)是一套被反復(fù)使用、多數(shù)人知曉的、經(jīng)過分類編目的、代碼設(shè)計(jì)經(jīng)驗(yàn)的總結(jié)。使用設(shè)計(jì)模式是為了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性。毫無疑問,設(shè)計(jì)模式于己于他人于系統(tǒng)都是多贏的;設(shè)計(jì)...
摘要:忍者級(jí)別的函數(shù)操作對(duì)于什么是匿名函數(shù),這里就不做過多介紹了。我們需要知道的是,對(duì)于而言,匿名函數(shù)是一個(gè)很重要且具有邏輯性的特性。通常,匿名函數(shù)的使用情況是創(chuàng)建一個(gè)供以后使用的函數(shù)。 JS 中的遞歸 遞歸, 遞歸基礎(chǔ), 斐波那契數(shù)列, 使用遞歸方式深拷貝, 自定義事件添加 這一次,徹底弄懂 JavaScript 執(zhí)行機(jī)制 本文的目的就是要保證你徹底弄懂javascript的執(zhí)行機(jī)制,如果...
閱讀 3183·2021-11-08 13:18
閱讀 2317·2019-08-30 15:55
閱讀 3626·2019-08-30 15:44
閱讀 3095·2019-08-30 13:07
閱讀 2802·2019-08-29 17:20
閱讀 1975·2019-08-29 13:03
閱讀 3448·2019-08-26 10:32
閱讀 3246·2019-08-26 10:15