WeHelp
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)