淺談 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 宣告的變數但卻又可以正常取得到值的原因。

參考文獻

0%