動圖學 JavaScript 之:聲明提高(Hoisting)

背景

JS 因爲語言設計的缺陷(工期不夠?),裏面有一些堪稱神奇的特性,初學者碰到後可能會滿臉黑人問號,今天要介紹的就是其中的一個特性:聲明提高(Hoisting)。javascript

若是你是一個 JS 新手,有時候會碰到 undefined 或者 ReferenceErrors 錯誤,而聲明提高有可能就是罪魁禍首。java

聲明提高經常被解釋爲:把變量和函數放到文件的頂部,雖然表面上看起來是這樣,但事實卻不是如此。git

編譯階段

當 JS 引擎開始解析咱們的腳本,第一件事就是爲咱們代碼中的變量 設置內存。這個階段代碼還 沒有運行,只是在爲後面的代碼執行作準備,這個階段就是 編譯階段github

這一階段的一部分工做就是 找到全部的聲明,並用合適的做用域將它們關聯起來(有關做用域的部分咱們下篇再講)。函數

好比 var a = 2; JS 引擎會將其看做兩個聲明:var a;a = 2;。第一個定義聲明在編譯階段進行,第二個賦值聲明會被 留在原地 等待執行階段。oop

而在編譯階段中,函數聲明和變量的聲明存儲方式是不一樣的。spa

函數聲明的存儲方式

函數聲明的存儲,在內存中存儲的是整個函數的引用。(注意 函數聲明函數表達式 的區別)翻譯

下圖的代碼,咱們先看其中的函數 sum 的聲明:設計

1-functions.gif

let 和 const 聲明的變量的存儲方式

變量的存儲不太同樣。ES6 中引入了兩個新的關鍵字:letconst,凡是用這兩個關鍵字定義的變量,存儲的值爲 uninitializedcode

2-let-and-const.gif

var 聲明的變量的存儲方式

var 聲明的變量,存儲的時候默認值爲 undefined

3-var.gif

執行階段

如今編譯階段已經完成,JS 引擎該執行代碼了。咱們在文件頂部添加三個 console.log 語句。

提早調用函數

上面講過,在編譯階段因爲 函數聲明 存儲的是整個函數的引用,因此即便在函數聲明以前也能夠調用函數。

4-console.gif

提早使用 var 定義的變量

若是咱們提早使用 city 這個變量,就會打印出 var 關鍵字定義的默認值 undefined。而大多數狀況下,這個行爲是使人困惑的,由於你並不指望它的值是 undefined

5-console-var.gif

提早使用 const 和 let 定義的變量

正是爲了解決 var 的問題,因此纔會有了 constlet,當咱們提早訪問它們定義的變量的默認值 uninitialized 時,就會拋出 ReferenceError 。這個行爲有一個霸氣的名字:臨時死區(Temporal Dead Zone),即你不能在這個變量初始化前訪問它。

6-console-let.gif

繼續執行賦值操做

當 JS 引擎繼續往下解釋代碼時,解釋到某一行有賦值語句時,即會將內存中的值覆蓋爲代碼中定義的值。

7-go-on.gif
(上圖中編號應該是 7 哈~)

總結

來回顧一下:

  1. 函數和變量在 編譯階段 會將聲明部分存儲在內存中,這就是 聲明提高 (其中還涉及到 執行上下文 的概念,咱們下篇再講)
  2. 函數聲明存儲的是整個函數的引用,var 聲明的變量存儲的默認值是 undefinedletconst 聲明的變量的默認值爲 uninitialized

不知道有沒有講明白呢?本文翻譯於做者 Lydia Hallie 的系列文章,部份內容做了詳細解釋,下面還有三篇,歡迎訂閱公衆號關注更新哈:

  • 動圖學 JS 之:做用域鏈(Scope Chain)
  • 動圖學 JS 之:事件循環(Event Loop)
  • 動圖學 JS 之:JavaScript 引擎

參考資料


本文首發於公衆號:碼力全開(codingonfire)

關注並回復 副業, 獲取技術人的副業祕籍

codingonfire.jpg

相關文章
相關標籤/搜索