相信你們在面試的過程當中常常遇到查看執行順序的問題,如setTimeout,promise,async await等等,各類組合,是否是感受頭都要暈掉了,其實這些問題最終仍是考察你們對js的運行機制是否掌握牢固,對promise,async的原理是否掌握,萬變不離其宗,此次就來完全搞懂它。
js引擎也是程序,是屬於瀏覽器的一部分,由瀏覽器廠商自行開發。從頭至尾負責整個JavaScript程序的編譯及執行過程面試
瀏覽器在渲染的過程當中,首先按順序加載由<script>標籤分割的js代碼塊,加載js代碼塊完畢後,須要js引擎進行解析。不管是外部腳本文件(不異步加載)仍是內部腳本代碼塊,都是同樣的原理,而且都在同一個全局做用域中。JavaScript被歸類爲「動態」或「解釋執行」語言,因此它無需提早編譯,而是由解釋器實時運行chrome
js引擎執行過程分爲三個階段:編程
js腳本代碼塊加載完畢後,會首先JS的解釋階段。該階段主要過程以下:數組
最終計算機執行的就是機器碼。promise
爲了提升運行速度,現代瀏覽器通常採用即時編譯(JIT-Just In Time compiler)瀏覽器
即字節碼只在運行時編譯,用到哪一行就編譯哪一行,而且把編譯結果緩存(inline cache)緩存
這樣整個程序的運行速度能獲得顯著提高。安全
並且,不一樣瀏覽器策略可能還不一樣,有的瀏覽器就省略了字節碼的翻譯步驟,直接轉爲機器碼(如chrome的v8)閉包
這裏我理解爲js爲解釋型語言,由解釋器實時運行,通俗的說就是預處理完以後立刻執行,一邊編譯一邊執行
function bar() { var B_context = "Bar EC"; function foo() { var f_context = "foo EC"; } foo() } bar()
這段函數通過詞法解析,語法解析階段以後,就開始進入預編譯並執行,以下:異步
分析一段簡單的代碼,幫助咱們理解建立執行上下文的過程,以下:
function fun(a, b) { var num = 1; function test() { console.log(num) } } fun(2, 3)
這裏咱們在全局環境調用fun函數,建立fun執行上下文,這裏爲了方便你們理解,暫時不講解做用域鏈以及this指向,以下:
funEC = { //變量對象 VO: { //arguments對象 arguments: { a: undefined, b: undefined, length: 2 }, //test函數 test: <test reference>, //num變量 num: undefined }, //做用域鏈 scopeChain:[], //this指向 this: window }
注:建立變量對象發生在預編譯階段,但還沒有進入執行階段,該變量對象都是不能訪問的,由於此時的變量對象中的變量屬性還沒有賦值,值仍爲undefined,只有進入執行階段,變量對象中的變量屬性進行賦值後,變量對象(Variable
Object)轉爲活動對象(Active Object)後,才能進行訪問,這個過程就是VO –> AO過程。
創建做用域鏈
做用域鏈由當前執行環境的變量對象(未進入執行階段前)與上層環境的一系列活動對象組成,它保證了當前執行環境對符合訪問權限的變量和函數的有序訪問。
理清做用域鏈能夠幫助咱們理解js不少問題包括閉包問題等,下面咱們結合一個簡單的例子來理解做用域鏈,以下:
var num = 30; function test() { var a = 10; function innerTest() { var b = 20; return a + b } innerTest() } test()
在上面的例子中,當執行到調用innerTest函數,進入innerTest函數環境。全局執行上下文和test函數執行上下文已進入執行階段,innerTest函數執行上下文在預編譯階段建立變量對象,因此他們的活動對象和變量對象分別是AO(global),AO(test)和VO(innerTest),而innerTest的做用域鏈由當前執行環境的變量對象(未進入執行階段前)與上層環境的一系列活動對象組成,以下:
innerTestEC = { //變量對象 VO: {b: undefined}, //做用域鏈 scopeChain: [VO(innerTest), AO(test), AO(global)], //this指向 this: window }
在這裏咱們順便思考一下,什麼是閉包?
咱們先看下面一個簡單例子,以下:
function foo() { var num = 20; function bar() { var result = num + 20; return result } bar() } foo()
我這裏直接以瀏覽器解析,以瀏覽器理解的閉包爲準來分析閉包,以下圖:
如上圖所示,chrome瀏覽器理解閉包是foo,那麼按瀏覽器的標準是如何定義閉包的,我總結爲三點:
肯定this指向在全局環境下,全局執行上下文中變量對象的this屬性指向爲window;函數環境下的this指向卻較爲靈活,需根據執行環境和執行方法肯定