1. 程式人生 > >研究JS執行機制之重新認識JavaScript(1) ———— 認識Js執行上下文與執行機制

研究JS執行機制之重新認識JavaScript(1) ———— 認識Js執行上下文與執行機制

關於這個系列

javascript語言有很多奧祕,譬如其執行機制,內部原理,在歷史的這一段日子裡,它不僅帶來了複雜的互動效果和充分的效能效益,而且吸引了越來越多的開發者加入其中,但是隨著時間程序的發展,很多時候開發者們因為花樣繁複的JS庫與框架而忽略了JS本身的內部機制。無可厚非,這樣做是增加了效率,但是隨著Web的蓬勃發展和JS社群的踴躍貢獻,JS的思想和創造年年翻新,現有的一切被淘汰也是時間的問題。所以開發者花費時間去研究其語言的內部機制是一個不被社會洪流淘汰的最佳選擇,而本系列恰好記錄了本人對於JS的理解和思考,會陸續將所學更新於此,希望對大家有所幫助。

受水平和技術有限,靈感和部分例子來自於Github裡《JavaScript開發者應懂的33個概念》系列集合,有興趣的可以在github裡面找到這個合集檢視完整的講解。

執行上下文

這是一個很難具體闡述的概念,而且繞口的文字也讓很多人費解,我也很難闡述他究竟是個什麼東西,為什麼取了個這樣的名字,但是我認為執行上下文就是javascript所執行的整個環境,它將整個js檔案的一些段落或某一個段落包裹起來,分別執行,可以把它理解為某一段程式碼是在怎麼樣的一個地方去執行的,如果感覺非常費解,其實你可以把整個js檔案比喻成一個房屋,房屋裡面擁有著不同的居住環境,不信你可以看看執行上下文的種類在房屋內充當了什麼樣的位置:

1.全域性執行上下文

全域性執行上下文就是屬於最外層的上下文,簡單的說就是除了函式裡面的上下文,初始化全域性執行上下文的時候它會做兩件事情,會建立一個window物件,並且把this指向window物件,整個全域性環境都屬於全域性執行上下文的環境,怎麼去理解呢?想象一下剛剛上面我所說的房屋,對於整個JS檔案而言他就是整個房屋,而全域性執行上下文就好像人所能夠移動的區域,客廳,廚房和飯廳就像一段段程式碼,我們的瀏覽器或者node環境也就是查房員,他負責查房後彙報房間裡擁有什麼東西,直到從門口開始把每一個房間都走完。

2.函式執行上下文

函式執行上下文就是全域性執行上下文所不能執行的地方,它往往存在與函式內部,全域性在遇到這種環境時會避開它,就好像房間裡的魚缸,鳥窩,它們都是魚和鳥活動的區域,在呼叫整個函式時,查房人就會往裡面放上相應的動物,直到它們把整個環境走完以後再取出來,這些動物不僅有自己的語言,行為和動作,而鳥籠,魚缸,狗窩它們大小也不一,當關上門時它們的世界就和整個房屋隔絕了開來,我們稱這一些地方叫做函式執行上下文。

3.eval函式上下文

eval函式會將一段字串當做JS程式碼來執行,而它自己也有屬於自己的上下文,這就好像一個便攜帳篷,在需要的時候把它在房屋裡面開啟,擁有了屬於自己內部的區域;

執行棧

剛剛說了執行上下文的概念,但是不少人會注意到有一個缺陷,就是我們如何把魚放入魚缸,鳥放入鳥窩跑一個遍呢?或者說我魚缸裡面的蝦窩,狗窩毛毯上的跳蚤窩又是怎麼去檢視的呢?其實這就引入了我們執行棧的概念;而管理員就是通過這個東西把它們放入對應的地方彙報資訊的。

要搞懂執行棧,首先要理解棧是一個什麼東西,有人會說棧是一種基本資料結構,先入先出,但本人就經常把佇列和棧搞混(可能比較愚笨),但是笨有笨的方法,我可以想象,我把棧看成是軍隊訓練時要爬的人牆,每次要爬山牆的人都是最後一個進入人牆,但是確實最先一個離開人牆,執行棧也是一樣,全域性執行上下文就相當於棧的底部,而每次執行到某個函式,其內部的函式上下文就會跳到全域性的身上,執行完成便翻過這道牆,當然函式上下文裡面的函式執行也是一樣,他會跳到上一個函式上下文上邊,這就形成了一個FQ的機制直到整個牆翻完為止。

對於常理來說,是這樣,但是總有一些奇怪的人,想自己組成一個小團隊,他們為了不影響別人,決定在大部隊翻過牆以後再自己FQ,這些小團隊有可能是一個人也有可能是幾個人,而這就牽扯到了JS的非同步

非同步

非同步是一個特殊的概念,在javascript中,整個語言的執行是單執行緒的,就是一次只能夠做一次事情,而一些特殊的事情為了不影響其他事情的執行他們選擇等待,當其他事情執行結束時他再去執行自己,一切都是這麼的規範和有理,和現實中的混亂相距甚遠;

而這一批有禮貌的事情是哪一些呢?

1.setTimeout 2.promise 3.事件繫結 4.ajax 5.回撥函式

他們都很有禮貌,每次都會等到執行完成後再去執行自身,至少我之前是這麼認為的;

慢慢的和他們相處長了以後,我發現,有一些偽君子,他們雖然也在最後執行,但是他們會插到所有有禮貌的人最前面,也就是說,他們在所有非同步的成員裡面首先執行,而這就牽扯到了微任務和巨集任務的問題了

而微任務就是這一批偽君子,這使我不得不介紹一下他們:

micro-task(微任務):Promise,process.nextTick,Object.observe

下面這張圖是從掘金《這一次,徹底弄懂 JavaScript 執行機制》一文中copy所得,方便大家理解微任務和巨集任務

事件迴圈,巨集任務,微任務的關係如圖所示

此文到這裡就結束了,相信大家也初步理解了機制,很多東西需要程式碼才能輔助理解,程式碼我會在之後補上,先告一段落