1. 程式人生 > 其它 >番外:透過JS程式碼看本質 - 04 從執行上下文物件入手看宣告提前

番外:透過JS程式碼看本質 - 04 從執行上下文物件入手看宣告提前

技術標籤:番外:透過JS程式碼看本質javascript

從執行上下文物件入手看宣告提前

資料

涉及到的關鍵詞

  • 執行上下文(執行環境)
  • 活動物件、變數物件
  • arguments、宣告提前
  • 作用域鏈

一、執行上下文(Execution Contexts)

(1) 為什麼要有執行上下文

  • 每個函式都有對應的執行環境,它定義了變數或者函式有權訪問的資料,決定他們各自的行為
  • 執行上下文(執行環境)是存在的物件,是規範中的存在和引擎上實現,JS無法訪問,無法操作,只不過大家都這麼叫,上一篇講的是函式環境,是記憶體上的實現,要注意區分
  • 每個函式有自己的函式環境,函式環境都有對應的執行上下文(執行環境),函式環境是棧資料結構,那麼執行上下文也應該是棧資料結構的,因為當恢復函式環境時,自然也要恢復執行上下文(執行環境)

(2) 什麼時候開始建立執行上下文

每次執行函式前,會對函式進行預編譯,這個時間段就在建立執行上下文。

執行上下文包含:

  • 變數物件(Variable Objecct),儲存宣告的變數和函式的具體位置
  • this value,確定this的值
  • 作用域鏈,確定能夠訪問的變數和函式

執行上下文也分:

  • 全域性執行上下文
  • 函式執行上下文

(3) 建立執行上下文的過程

宣告變數 var0
函式 A() {
    宣告變數 var1
    函式 B()
    宣告變數 var2
}

函式 B() {
    宣告變數 var3
    函式 C()
    宣告變數 var4
}

函式 C() {
    宣告變數 var5
}

//執行函式 A
函式A()
  1. 程式碼一開始,先是全域性執行上下文物件
globalExecutionContent = {
  VO: {
    arguments: undefined //預設是 undefined
  },
  Scope: [globalContent.VO],
  this: window
}
  1. 呼叫函式A,建立A函式的執行上下文物件
AExecutionContent = {
  VO: {
    arguments: undefined //預設是 undefined
  },
  Scope: [AExecutionContent.VO, globalExecutionContent.VO],
this: window }
  1. 其它函式的執行上下文物件也是如此,注意作用域鏈的最左端是當前執行上下文物件的變數物件,其後依次的是上層的執行上下文物件的變數物件

(4) 變數物件和活動物件

前面說過每個執行上下文都有一個對應的變數物件,但是注意執行上下文也是棧新式的結構,當前函式呼叫另外一個函式時,肯定也要儲存當前的執行上下文,並建立新的上下文及其變數物件

此時,最上層的執行上下文和它的變數物件都是被 “啟用的”,因為要執行當前環境程式碼必須訪問執行上下文和它的變數物件

這樣看其它的下層的執行上下文和它的變數物件都是 “休息狀態”,在當前環境不需要訪問它們

二、變數物件怎麼建立的

這一小節涉及宣告提前

進入函式並建立執行上下文物件時,對函式進行一掃描

  1. 掃描函式的所有形參,並將形參名稱 和對應值組成的鍵值對作為變數物件VO的屬性。如果沒有傳遞對應的實參,將undefined作為對應值。如果形參名為arguments,將覆蓋arguments(這裡和上面執行環境對應)

  2. 掃描函式程式碼中所有的函式宣告(注意是函式宣告,函式表示式不算)
    將函式名和對應值(指向記憶體中該函式的引用指標)組成組成的鍵值對作為變數物件VO的屬性
    如果變數物件VO已經存在同名的屬性,則覆蓋這個屬性

  3. 掃描函式程式碼中所有的變數宣告
    由變數名和對應值(此時為undefined) 組成,作為變數物件的屬性
    如果變數名與已經宣告的形參或函式相同,此時什麼都不會發生,變數宣告不會干擾已經存在的這個同名屬性。

三、作用域和作用域鏈

(1) 作用域

  1. 變數(或者說標識)的作用域是程式原始碼中定義這個作用域的區域
  2. 作用域是描述這個變數起作用的程式碼範圍

(2) 作用域鏈

JS是靜態作用域,這意味這一旦程式碼確定,作用域確定,並且不會改變

this不是作用域的一部分,可以從上一小節的執行上下文的作用域鏈可以看到 this和變數物件和作用域鏈是同級的

JS函式內部還能定義函式,內部函式還能訪問上層函式的變數和函式(注意這個上層,是定義函式時的程式碼的上層環境或者說上層函式)

作用域鏈就像一個數組一樣,它的最前端表示當前函式的作用域,往後就是上層函式的作用域,一直到全域性環境,當需要訪問變數或者函式時,查詢方式就是按照這樣的路徑查詢