五分鐘輕鬆暸解「提升(Hoisting)」!

「獻給 JS 新手的五門底層運作課」系列文章之三

Coding Monster
魔鬼藏在程式細節裡
5 min readJul 1, 2019

--

Photo by Devon Divine on Unsplash

p.s. 如果你還不暸解什麼是執行環境 (Execution Context),建議你先閱覽上一篇文章,有了相關的基礎之後,再閱讀本篇文章。

在上一篇『秒懂!JavaScript 執行環境與堆疊』中,我們淺談了:

  • 執行環境 (Execution Context)
  • 執行堆疊 (Execution Stack)

同時也有提到,執行環境 在建立時會經歷兩個階段,分別是:

  1. 創造階段 (Creation Phase)
  2. 執行階段 (Execution Phase)

執行階段 (Execution Phase) 顧名思義,就是一行一行地執行程式碼,這沒什麼困難的。不過在那之前,還有一個相當重要的階段,稱作創造階段!傳說中的 提升 (Hoisting) 就是在這個階段發生的。

那究竟創造階段(Creation Phase) 是在創造什麼呢?

執行環境物件

還記得上一章,我提過一個叫做 執行環境物件 傢伙嗎?

每個執行環境都會配有一個執行環境物件 (Execution context object)
負責紀錄該環境中需要用到的各種資料。

由於需要紀錄的資料相當得多,所以 執行環境物件 就必須好好地為這些資料分類,讓 執行環境 能更快速、更明確地取得它需要的資料。

因此,每個 執行環境物件 都有 3 個屬性 (Property),分別是:

  • 變數物件 (Variable Object)
  • 作用域鏈 (Scope Chain)
  • “This” 變數 (“This” Variable)

我知道這三個屬性聽起來都很不友善,但概念其實非常地簡單,相信你讀完這一系列的文章後,就會很有感覺。

這個章節我們會著重在 執行環境物件 的第一個屬性:

Variable Object 變數物件。

Variable Object 變數物件

剛剛我們有提到:「Variable ObjectScope Chain“This” variable 三大巨頭,分工合作地為 執行環境 記錄許多相當重要的資料。」對吧?

接下來,我們會用平易近人的方式介紹 Variable Object 究竟負責哪些資料,抓點零食,我們開始吧!

Variable Object 負責三件事:

  • 建立 Argument Object,存放所有我們打算送進函式的引數 (Argument)
  • 掃描程式碼中是否有函式宣告 (Function Declaration):如果有,就為每一個函式建立一個新屬性,其值指向該函式在記憶體中的位址。
  • 掃描程式碼中是否有變數宣告 (Variable Declaration):如果有,就為每一個變數建立一個新屬性,並將該屬性的值初始為 undefined

而最後兩項在程式界中,有一個響叮噹的名號:Hoisting(提升)。

提升 Hoisting

Hoisting(提升) 聽起來好像很牛,其實概念非常簡單:

  • 變數與函式,在進入執行階段前,其實就已經完成宣告。
  • 這種「將變數宣告與函式宣告的動作,提升到程式碼最頂端」的行為,就是 Hoisting(提升)

Hoisting(提升) ,其實也解答了許多 Javascript 初學者的疑惑:

為什麼有些函式在宣告式之前就可以進行呼叫?

答案就是:
這些函式早就在執行階段前,就已經被宣告並儲存在 Variable Object 中。

函式的 Hoisting(提升)變數的 Hoisting(提升),差在哪裡呢?

  • 函式在建立階段時,就已經獲得明確的定義,另一方面,變數在創造階段還不會被賦予它們應有的值,而是統一初始化為 undefined,直到執行階段時,變數才會隨著程式碼的讀取而被賦予明確的值。

本堂精華:1 分鐘課後複習

  • 執行環境 的建立會經歷兩個階段:1) 創造階段、2)執行階段。
  • 每個 執行環境 都會有一個 執行環境物件,幫忙記錄該環境中的資料。
  • 執行環境物件有三個屬性:變數物件 (Variable Object)作用域鏈 (Scope Chain)“This” 變數
  • 變數物件 (Variable Object) 有三個任務:協助紀錄函式引數、完成函式與變數宣告,並將所有變數值初始為 undefined
  • Hoisting(提升) 指的是:變數與函式,在進入執行階段前,其實就已經完成宣告。
  • 函式的 Hoisting(提升) 與變數的 Hoisting(提升),差在變數在這個階段還未獲得明確的定義,統一初始為 undefined,而函式已獲得完整定義。

Takeaway

暸解為什麼有些函式和變數在宣告式之前就可以進行呼叫?

預告:

下一章節,我們會聊到執行環境物件中的第二個屬性:作用域鏈 (Scoping Chain),為何它會被形容成家族族譜?我們將由淺入深,讓你能瞬間掌握這些抽象到不行、卻重要到不行的「JS 底層運作原理」。

如果這篇文章有幫助到你的話,是我最大的榮幸。
請用長按「拍手 (Clap)」的方式讓我知道你對這篇文章的喜愛!
也歡迎路過的大神們能給予技術方面的建議,謝謝你們的觀看。

--

--