淺談 var 與 let 的差異以及有無宣告變數的差異
前言
在網路上其實滿多人都有講過 var
與 let
的差異以及有無宣告變數的差異,所以我這邊也來談一下這兩個最大的差異以及有無宣告變數的差異。
有無宣告變數的差異
其實應該打「有無使用 var 的差異」才對,但是中文來講確實是宣告變數的差異,在說明這個差異之前,必須先瞭解到幾個東西
- 全域環境
- 全域物件
但是上面這兩個觀念在我先前的「[JS奇怪的世界]No.3 全域環境與全域物件」有介紹過,所以這邊就不多做介紹,所以這邊就直接來講重點。
在一般狀況下使用 var
與沒有使用 var
最大差別在於「可不可以被 delete
刪除」。
首先 delete
是一個刪除物件屬性的語法,在 MDN 這邊有介紹,因此我們可以透過以下語法了解有無宣告 var
的差異
1 | var a = 'a'; |
透過上面我們可以了解到一件事情,如果沒有使用 var
宣告的變數「會被當作物件屬性新增」的方式新增,因此就會強烈建議你變數一定要使用 var
宣告,否則可以被刪除的變數是很容易出現系統上的 bug。
這邊我們先不講 let
、const
。
作用域
接下來先讓我們了解一下 var
與 let
、const
的作用域,通常來講我們都會建議使用者可以將過去用 var
宣告的變數通通改成 let
,但 let
與 var
差異在哪裡?
先來講講 var
的作用域,var
的作用域是「函式作用域」,函式作用域的意思是說,它是依照函式來決定自己的變數作用域,舉例來講
1 | function fuA() { |
我們無法直接取得宣告在 fuA
中的變數,儘管它已經呼叫並執行。
那 let
呢?let
的作用域是「區塊作用域」,最簡單的辨別的方式就是只要有「{ }
」花括號就是它的作用域範圍
1 | if(true) { |
如果這邊的範例你是拿函式當作比較那麼就會看不出所以然,因為作用域雷同。
1 | function fuA() { |
但若是使用 if
這類就會可以看出差異
1 | if(true) { |
所以使用 let
宣告變數的好處在於,我們可以確保不會污染到全域物件,因為只要不是在「函式內宣告」的變數,都會污染到全域物件,例如上述的 if
範例就會污染的全域物件
那你可以會問:「const
呢?」,其實與 let
相同。
在這邊就不提 Hoisting 了,下一次有機會再來講。
為什麼 let 無法從 window 中找到
在這邊這是一個很少很少有人提到的一件事情,首先在以往當我們輸入以下變數宣告後都可以在 window 下找到相關變數
1 | var aName = 'Ray'; |
但是當我們使用了 ES6 語法,也就是 let
或是 const
卻無法找到這個變數
1 | let aName = 'Ray'; |
但這邊我們若使用 aName
卻又可以正確取得已經宣告的變數
那麼這是什麼神奇的原因?在以往我們的觀念中,通常我們宣告的變數都會被加入到 window 中,但使用 let
宣告的變數卻無法在 window 底下找到,可以卻又可以正確的呼叫取得該變數的值,當你嘗試使用 delete aName
,你也會得到一個 false
無法刪除的警告。
那這是魔術嗎?其實不是。
這個原因是出在全域執行環境(Global space) 上面,首先這個全域執行環境其實是由兩個環境所組成的
- 全域物件 - Object Env
- 宣告環境 - Declare Env
因此全域執行環境(Global space) 其實是一個由雙環境組成的東西,一般來說我們是看不到 Declare Env 的。
所以 var
其實是基於 ObjectEnv 宣告並加入到 Declare Env,而 let
、const
則是只會宣告在 Declare Env 中,這也就是為什麼我們無法在 Window 上面看到由 let
、const
宣告的變數但卻又可以正常取得到值的原因。