JavaScript進階之執行上下文和執行棧

js引擎的執行過程

 

執行上下文和執行棧屬於js引擎的執行過程的預編譯階段。前端

執行上下文(Execution Context)

執行上下文是當前 JavaScript 代碼被解析和執行時所在環境的抽象概念。能夠理解爲當執行代碼時作的準備工做。git

 

執行上下文按照運行環境被分紅三類:github

  • 全局執行上下文(JS代碼加載完畢後,進入代碼預編譯即進入全局環境)瀏覽器

  • 函數環境執行上下文(函數調用執行時,進入該函數環境,不一樣的函數則函數環境不一樣)安全

  • eval執行上下文(不建議使用,會有安全,性能等問題)ide

對於每一個執行上下文,都有三個重要屬性:函數

  • 變量對象(Variable object,VO),存儲了在上下文中定義的變量和函數聲明。在全局執行上下文中指window,函數執行上下文中就是活動對象(AO)
  • 做用域鏈(Scope chain),和原型鏈相似,當查找變量時,會先在當前上下文變量對象中查找,若是沒有會去父級上下文變量對象中查找,直到全局上下文。這樣由多個執行上下文的變量對象構成的鏈表就叫作做用域鏈
  • this

VO:變量對象(Variable object);post

AO:活動對象(activation object),當進入函數執行上下文時,這個函數執行上下文的變量對象纔會被激活,因此才叫 activation object,而只有被激活的變量對象,也就是活動對象上的各類屬性才能被訪問(進入函數上下文時建立活動對象)。性能

執行棧

執行棧,也叫調用棧,具備 LIFO(後進先出)結構,用於存儲在代碼執行期間建立的全部執行上下文。this

首次運行JS代碼時,會建立一個全局執行上下文並Push到當前的執行棧中。每當發生函數調用,引擎都會爲該函數建立一個新的函數執行上下文並Push到當前執行棧的棧頂。

根據執行棧LIFO規則,當棧頂函數運行完成後,其對應的函數執行上下文將會從執行棧中Pop出,上下文控制權將移到當前執行棧的下一個執行上下文。

全局執行上下文在瀏覽器關閉或者標籤關閉時纔會出棧。

 1 var a = 'Hello World!';
 2 
 3 function first() {  
 4   console.log('Inside first function');  
 5   second();  
 6   console.log('Again inside first function');  
 7 }
 8 
 9 function second() {  
10   console.log('Inside second function');  
11 }
12 
13 first();  
14 console.log('Inside Global Execution Context');
15 
16 // Inside first function
17 // Inside second function
18 // Again inside first function
19 // Inside Global Execution Context

 

建立過程

 

 

建立階段就是肯定三個重要上下文屬性的過程。

建立變量對象

  1. 建立arguments對象,檢查當前上下文中的參數,創建該對象的屬性與屬性值,僅在函數環境(非箭頭函數)中進行,全局環境沒有此過程

  2. 檢查當前上下文的函數聲明,按代碼順序查找,將找到的函數提早聲明,若是當前上下文的變量對象沒有該函數名屬性,則在該變量對象以函數名創建一個屬性,屬性值則爲指向該函數所在堆內存地址的引用,若是存在,則會被新的引用覆蓋。

  3. 檢查當前上下文的變量聲明,按代碼順序查找,將找到的變量提早聲明,若是當前上下文的變量對象沒有該變量名屬性,則在該變量對象以變量名創建一個屬性,屬性值爲undefined;若是存在,則忽略該變量聲明

變量提高的緣由:

在建立階段,函數聲明存儲在環境中,而變量會被設置爲 undefined(在 var 的狀況下)或保持未初始化(在 let 和 const 的狀況下)。因此這就是爲何能夠在聲明以前訪問 var 定義的變量(儘管是 undefined ),但若是在聲明以前訪問 let 和 const 定義的變量就會提示引用錯誤的緣由。這就是所謂的變量提高。

從建立順序看,函數提高優先於變量提高。

建立做用域鏈

由多個執行上下文的變量對象構成的鏈表就叫作做用域鏈。

當函數建立的時候,函數有一個內部屬性會保存全部父變量對象到其中。

進入函數上下文,建立 VO/AO 後,就會將活動對象添加到做用鏈的前端。

因此:

  • 做用域鏈的第一項永遠是當前做用域(當前上下文的變量對象或活動對象);

  • 最後一項永遠是全局做用域(全局執行上下文的活動對象);

  • 做用域鏈保證了變量和函數的有序訪問,查找方式是沿着做用域鏈從左至右查找變量或函數,找到則會中止查找,找不到則一直查找到全局做用域,再找不到則會拋出引用錯誤。

肯定This指向

全局執行上下文中變量對象的this屬性指向爲window,函數上下文則較爲複雜,之後會詳細介紹。

執行階段

完成對全部變量的分配,最後執行代碼。

總覽

 參考文檔:

      理解JavaScript 中的執行上下文和執行棧

      JavaScript深刻之執行上下文棧

      JavaScript深刻之變量對象

      JavaScript深刻之做用域鏈

      js引擎的執行過程(一)

相關文章
相關標籤/搜索