淺談 var 與 let 的差異以及有無宣告變數的差異

前言

在網路上其實滿多人都有講過 varlet 的差異以及有無宣告變數的差異,所以我這邊也來談一下這兩個最大的差異以及有無宣告變數的差異。

有無宣告變數的差異

其實應該打「有無使用 var 的差異」才對,但是中文來講確實是宣告變數的差異,在說明這個差異之前,必須先瞭解到幾個東西

  • 全域環境
  • 全域物件

但是上面這兩個觀念在我先前的「[JS奇怪的世界]No.3 全域環境與全域物件」有介紹過,所以這邊就不多做介紹,所以這邊就直接來講重點。

在一般狀況下使用 var 與沒有使用 var 最大差別在於「可不可以被 delete 刪除」。

首先 delete 是一個刪除物件屬性的語法,在 MDN 這邊有介紹,因此我們可以透過以下語法了解有無宣告 var 的差異

1
2
3
4
5
var a = 'a';
delete a; // false

b = 'b';
delete b; // ture

透過上面我們可以了解到一件事情,如果沒有使用 var 宣告的變數「會被當作物件屬性新增」的方式新增,因此就會強烈建議你變數一定要使用 var 宣告,否則可以被刪除的變數是很容易出現系統上的 bug。

這邊我們先不講 letconst

作用域

接下來先讓我們了解一下 varletconst 的作用域,通常來講我們都會建議使用者可以將過去用 var 宣告的變數通通改成 let,但 letvar 差異在哪裡?

先來講講 var 的作用域,var 的作用域是「函式作用域」,函式作用域的意思是說,它是依照函式來決定自己的變數作用域,舉例來講

1
2
3
4
5
function fuA() {
var a = 'a';
}
fuA();
console.log(a); // a is not defined

我們無法直接取得宣告在 fuA 中的變數,儘管它已經呼叫並執行。

let 呢?let 的作用域是「區塊作用域」,最簡單的辨別的方式就是只要有「{ }」花括號就是它的作用域範圍

1
2
3
4
5
if(true) {
let a = 'a';
}

console.log(a);

如果這邊的範例你是拿函式當作比較那麼就會看不出所以然,因為作用域雷同。

1
2
3
4
5
6
7
8
9
10
11
function fuA() {
var a = 'a';
}
fuA();
console.log(a); // a is not defined

function fuB() {
let b = 'b';
}
fuB();
console.log(b); // b is not defined

但若是使用 if 這類就會可以看出差異

1
2
3
4
5
6
7
8
9
10
11
if(true) {
var a = 'a';
}

console.log(a); // a

if(true) {
let b = 'b';
}

console.log(b); // b is not defined

所以使用 let 宣告變數的好處在於,我們可以確保不會污染到全域物件,因為只要不是在「函式內宣告」的變數,都會污染到全域物件,例如上述的 if 範例就會污染的全域物件

var 宣告導致污染

那你可以會問:「const呢?」,其實與 let 相同。

在這邊就不提 Hoisting 了,下一次有機會再來講。

為什麼 let 無法從 window 中找到

在這邊這是一個很少很少有人提到的一件事情,首先在以往當我們輸入以下變數宣告後都可以在 window 下找到相關變數

1
2
var aName = 'Ray';
console.log(window);

window

但是當我們使用了 ES6 語法,也就是 let 或是 const 卻無法找到這個變數

1
2
let aName = 'Ray';
console.log(window);

找不到 let 宣告的變數

但這邊我們若使用 aName 卻又可以正確取得已經宣告的變數

無法在 Window 下找到卻可以取得變數

那麼這是什麼神奇的原因?在以往我們的觀念中,通常我們宣告的變數都會被加入到 window 中,但使用 let 宣告的變數卻無法在 window 底下找到,可以卻又可以正確的呼叫取得該變數的值,當你嘗試使用 delete aName,你也會得到一個 false 無法刪除的警告。

那這是魔術嗎?其實不是。

這個原因是出在全域執行環境(Global space) 上面,首先這個全域執行環境其實是由兩個環境所組成的

  • 全域物件 - Object Env
  • 宣告環境 - Declare Env

因此全域執行環境(Global space) 其實是一個由雙環境組成的東西,一般來說我們是看不到 Declare Env 的。

所以 var 其實是基於 ObjectEnv 宣告並加入到 Declare Env,而 letconst 則是只會宣告在 Declare Env 中,這也就是為什麼我們無法在 Window 上面看到由 letconst 宣告的變數但卻又可以正常取得到值的原因。

參考文獻

Liker 讚賞 (拍手)

如果這一篇筆記文章對你有幫助,希望可以求點支持或 牡蠣 鼓勵 (ノД`)・゜・。

Liker 是一個按讚(拍手)的讚賞機制,每一篇文章最多可以按五下拍手,過程你只需要登入,如果你願意按個讚,對於創作者來講是一個莫大的鼓勵與支持。

Google AD

撰寫一篇文章其實真的很花時間,如果你願意「關閉 Adblock (廣告阻擋器)」來支持我的話,我會非常感謝你 ヽ(・∀・)ノ