JavaScript 中的 let、const、var 差別
2023-08-12 18:54:34
### JavaScript 中變數宣告 let、const 以及 var 的差別
1. 在 ES6 以前, JavaScript 變數宣告 `var` 有效範圍的最小單位是以 function 做分界,而這個有效範圍我們通常稱它為「Scope」。
1-1. 在 `var` 宣告之前就存取對應的變數,會出現 `undefined`
2. 與 `var` 不同的是,`let` 以及 `const` 它們的 Scope 是透過大括號 `{}` 來切分的。
2-1. 在 `let` 與 `const` 宣告之前就存取的變數或常數,會出現 `ReferenceError` 錯誤;
3. 在 ES6 以後,有 `let` 與 `const` 分別定義「變數」與「常數」。
---
#### 1. var 宣告變數的有效範圍
#### 以下程式碼範例取自書籍 0 陷阱!0 誤解!8 天重新認識 JavaScript
#### 1-1 在 function 內有 ` x ` 變數宣告的狀況
``` js
var x = 1;
var example = function(y) {
var x = 10;
return x + y;
};
console.log(example(5));
// 15
console.log(x);
// 1
```
因為在函式 `example` 裡面定義了變數 `x` ,所以當我們執行 `example(5)` 時,會將 `5` 作為參數傳入 `example` 的 `y`。
那麼 `return x + y` 的結果自然就是 `10+5` 的 `15` 了。
---
#### 1-2 在 function 內沒有 `x` 變數宣告的狀況
``` js
var x = 1;
var example = function(y) {
return x + y;
};
console.log(example(5));
// 6
```
如果 function 內部沒有 `var` 宣告 `x` ,就會一層一層往外找,直到全域變數為止。
當我們執行 `example` 函式時,這個函式接受一個參數 `y`,並會回傳 `x+y` 的結果。
這時候的 `x` 會是全域範圍中的 `x`,其值為 `1`。
所以當`console.log(example(5));`時,該函式會回傳`x+y`,為`1+5`,結果為`6`。
---
#### 1-3 在 function 內沒有 `x` 變數宣告的狀況
``` js
var x = 1;
var example = function(y) {
x = 5
return x + y;
};
console.log(example(5));
// 10
console.log(x);
// 5
```
因為變數宣告 `var` 有效範圍的最小單位是以 function 做分界。
所以在 function 內沒有重新宣告 `x` 變數,使得 `x=5` 變更了外層的變數 `x`。
---
#### 1-4 提升(Hoisting)
#### 什麼是提升(Hoisting) ?
當你在一個 函式 內部宣告變數,不論這個宣告放在函式的哪個地方,該變數的宣告會被提升到該函式的最頂端。
所以,即使在某個語句之後宣告變數,但在這個語句執行之前該變數已經被宣告了。
但要特別注意的是,變數和函式的宣告會在 JavaScript 引擎執行程式碼時,它首先會進行一個預編譯的階段,
這階段會將所有找到的 var 宣告以及函式宣告,將它們先放在記憶體中,
***實際在程式碼的位置還是一樣,並不是真的會將你的程式碼物理上移動到其他位置。***
#### 在 `var` 宣告之前就存取對應的變數,會出現 `undefined`
``` js
var x = 1;
var example = function(y) {
console.log(x);
// undefined
var x = 5
return x + y;
};
console.log(example(5));
// 10
console.log(x);
// 1
```
因為變數提升的關係, 函式裡面的 `var x=5;` 會被分為兩個部分,變數宣告 `var x` 以及 `x=5` 。
變數宣告 `var x;` 會被提升到函式的頂部,所以 `example` 函式在執行時的結構會像以下這樣:
``` js
var example = function(y) {
var x;
console.log(x);
// undefined
x = 5;
return x + y;
};
```
所以,當 `console.log(x);`在函式內被執行時,`x` 的值是 `undefined`。
而 Javascript 這種特性,我們稱作「變數提升」( Variables Hoisting ),所以變數盡量都在 scope 最上面先宣告完成再使用。
---
#### 2. let、const 宣告變數的有效範圍
#### 2-1 `let` 以及 `const` 它們的 Scope 是透過大括號 `{}` 來切分的
``` js
let x = 1;
var example = function(y) {
let x = 10
return x + y;
};
console.log(example(5));
// 15
console.log(x);
// 1
```
這邊命名的兩個 `x` 變數,由於它們在不同的範圍(Scope)內,所以它們是獨立互不干擾。
全域範圍(Global Scope):
``` js
let x = 1;
```
函式範圍 (Function Scope):
``` js
let x = 10;
```
這個 `x` 只在 `example` 函式的範圍內有效。因此,當我們在函式內引用 `x` 時,
它指的是值為 `10` 的這個局部變數,而不是值為 `1` 的全域變數。
---
#### 2-2 在 `let` 與 `const` 宣告之前就存取的變數或常數,會出現 `ReferenceError` 錯誤;
#### 什麼是暫時性死區(Temporal Dead Zone, TDZ)?
意思是使用 `let` 以及 `const` 宣告變數在你宣告它們之前不會存在。
在這範圍內,被稱為暫時性死區(Temporal Dead Zone, TDZ),
變數無法被存取,否則會產生 `ReferenceError`。
``` js
var x = 1;
var example = function(y) {
console.log(x); // ReferenceError
let x;
x = 5;
return x + y;
};
```
我們將 `var` 用 `let` 替換,雖然 `let` 不會有變數提升的作用,但是呼叫 `emample` 時候,
會出現 `ReferenceError: Cannot access 'x' before initialization` 的錯誤訊息。
因為 `x` 在全域範圍已經被宣告為 `1` ,但在 `example` 函式內部,又用 `let` 重新宣告區域變數 `x` 。
從 `let x;` 這行之前, `x` 會處於 TDZ 內,此時不能存取 `x`。所以當你是試圖在 `let x ;` 之前用 `console.log(x);` 存取它時,會出現 `ReferenceError`。
---
#### 3. 在 ES6 以後,有 `let` 與 `const` 分別定義「變數」與「常數」。
#### 3-1 使用 let 定義變數:
``` js
let fruit = "apple";
console.log(fruit); // 輸出 "apple"
```
並且 let 定義變數值是可以更改的
``` js
fruit = "banana";
console.log(fruit); // 輸出 "banana"
```
但要注意的是,`let`不允許重複宣告。
``` js
let example = "Hello!";
let example= "Hi again!"; // 這會引發錯誤,因為 example 已經被宣告過了
```
---
#### 3-2 使用 const 定義常數:
``` js
const PI = 3.14159;
console.log(PI); // 輸出 3.14159
```
但要注意的是,`const` 定義的變數值是不可以更改,會產生錯誤訊息 `TypeError: Assignment to constant variable.`。
---
#### 參考資料
[0 陷阱!0 誤解!8 天重新認識 JavaScript!](https://www.books.com.tw/products/0010832387)
[在 JavaScript 中用 var, let, 以及 const 有什麼差別?什麼時候該用哪個?](https://www.explainthis.io/zh-hant/interview-guides/javascript/js-var-let-const-in-javascript)
[MDN - var](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var)
[MDN - let](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let)
[MDN - const](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const)
[MDN -Hoisting](https://developer.mozilla.org/en-US/docs/Glossary/Hoisting)
[JavaScript ES6:詳解 let、const、var 的差異](https://www.youtube.com/watch?v=Sj2iQEGwFDg&list=PL-g0fdC5RMboo-XNa2DzFvYg_9QWBIos6&index=3&ab_channel=%E5%BD%AD%E5%BD%AD%E7%9A%84%E8%AA%B2%E7%A8%8B)
[JavaScript ES6:宣告 let 變數、const 常數](https://www.youtube.com/watch?v=8KMIJg6K-AI&list=PL-g0fdC5RMboo-XNa2DzFvYg_9QWBIos6&index=2&ab_channel=%E5%BD%AD%E5%BD%AD%E7%9A%84%E8%AA%B2%E7%A8%8B)
點擊複製文章連結
X