在 JavaScript 中,var、let 和 const 都可以用來聲明變量,但它們在作用域、提升行為和可變性等方面存在顯著差異。
一、var
在ES6之前,
var是我們聲明變量的唯一選擇
1.1 作用域
- 函數作用域:使用
var聲明的變量,其作用域是其所在的函數。如果在函數外聲明,則是全局作用域。 - 全局作用域:在全局作用域中使用
var聲明的變量會成為全局對象(在瀏覽器中是 window)的屬性。 - 塊級作用域無效:
var聲明的變量在塊級作用域(如 if 或 for 塊)中不會被限制在塊內,而是提升到函數或全局作用域。
1.2 變量提升
使用var聲明的變量會被提升到其作用域的頂部,並且會被初始化為undefined。這意味着你可以在聲明變量之前訪問它,但它的值會是undefined。
console.log(x); // 輸出 undefined
var x = 10;
console.log(x); // 輸出 10
變量提升(Hoisting)是 JavaScript 中的一個特性,它指的是在 JavaScript 程序執行之前,變量和函數聲明會被“提升”到其所在作用域的頂部。
1.3 可變性
在JavaScript中,var的可變性指的是使用var聲明的變量可以在聲明後重新賦值,也就是説,變量的值是可以被改變的。這種特性使得var聲明的變量具有一定的靈活性,但也可能導致一些潛在的問題,比如變量被意外覆蓋或修改。
var x = 10;
console.log(x); // 輸出 10
x = 20;
console.log(x); // 輸出 20
1.4 var的缺點
1.4.1 函數作用域而非塊級作用域
var 聲明的變量具有函數作用域或全局作用域,而不是塊級作用域。這意味着變量的作用域是其所在的函數,而不是其所在的代碼塊(如 if 或 for 塊)。
if (true) {
var x = 10;
}
console.log(x); // 輸出 10
1.4.2 變量提升
var 聲明的變量會被提升到其作用域的頂部,並且會被初始化為 undefined。這意味着你可以在聲明變量之前訪問它,但它的值會是 undefined。
console.log(x); // 輸出 undefined
var x = 10;
console.log(x); // 輸出 10
這種變量提升行為可能導致以下問題:
- 意外的 undefined:在聲明之前訪問變量會導致其值為 undefined,這可能掩蓋一些錯誤。
- 難以理解的代碼:變量的聲明和初始化被分開了,可能導致代碼難以理解和維護。
1.4.3 可重新聲明
var 聲明的變量可以在同一個作用域內多次聲明,但只有第一次聲明會生效。這可能導致意外的變量覆蓋和難以追蹤的錯誤。
var x = 10;
var x = 20; // 有效,但不會報錯,x 的值為 20
console.log(x); // 輸出 20
這種行為可能導致以下問題:
- 重複聲明:在同一個作用域內多次聲明同一個變量,可能導致代碼難以維護。
- 意外覆蓋:重複聲明可能導致變量的值被意外覆蓋,從而引發錯誤。
1.4.4 全局變量污染
在全局作用域中使用 var 聲明的變量會成為全局對象(在瀏覽器中是 window)的屬性。這可能導致全局變量污染,尤其是在大型項目中。
var x = 10;
console.log(window.x); // 輸出 10
這種行為可能導致以下問題:
- 全局變量衝突:多個腳本或模塊可能意外地使用相同的全局變量名,導致衝突。
- 難以管理的全局狀態:全局變量的使用可能導致代碼難以維護和調試。
二、let
let 是 ES6(ECMAScript 2015)引入的新的變量聲明方式,它解決了 var 的一些問題。
2.1 作用域
塊級作用域:let聲明的變量只在它們被聲明的代碼塊內有效。代碼塊可以是任何由大括號 {} 包裹的代碼片段,例如 if 語句、for 循環、while 循環等。
if (true) {
let x = 10;
console.log(x); // 輸出 10
}
console.log(x); // 報錯,ReferenceError: x is not defined
2.2 變量提升
let的變量提升行為與var有所不同。let聲明的變量也會被提升到其作用域的頂部,但不會被初始化。這意味着在聲明之前訪問這些變量會拋出ReferenceError。
console.log(x); // 報錯:Cannot access 'x' before initialization
let x = 10;
console.log(x); // 輸出 10
2.3 暫時性死區
let聲明的變量在它們被聲明之前是不可訪問的。這種特性被稱為暫時性死區(Temporal Dead Zone, TDZ)。在TDZ中,變量已經被聲明,但尚未被初始化,因此任何嘗試訪問它的操作都會導致 ReferenceError。
console.log(y); // 報錯,ReferenceError: y is not defined
let y = 20;
console.log(y); // 輸出 20
2.4 可變性
可以在同一個作用域內重新賦值,但不能重新聲明。
let x = 10;
x = 20; // 有效,x 的值為 20
let x = 30; // 報錯:Identifier 'x' has already been declared
三、const
const 是 JavaScript 中用於聲明常量的關鍵字,它在 ES6 中被引入。const 的主要目的是聲明一個不可重新賦值的變量。
3.1 作用域
const 聲明的變量具有塊級作用域,這意味着它們只在聲明所在的塊(如 if 或 for 塊)內有效。
if (true) {
const x = 10;
console.log(x); // 輸出 10
}
console.log(x); // 報錯:x is not defined
3.2 變量提升
與let類似,const聲明的變量也會被提升到其作用域的頂部,但不會被初始化。因此,在聲明之前訪問這些變量會拋出 ReferenceError。
console.log(y); // 報錯:Cannot access 'y' before initialization
const y = 20;
console.log(y); // 輸出 20
3.3 可變性
const聲明的變量在聲明後不能被重新賦值。如果嘗試重新賦值,會拋出錯誤。
const z = 30;
z = 40; // 報錯:Assignment to constant variable.
雖然 const 聲明的變量不能被重新賦值,但如果你聲明的是一個對象或數組,你可以修改其屬性或元素。這是因為 const 只是防止變量被重新賦值,而不是防止對象或數組的內容被修改。
const obj = { name: "Alice" };
obj.name = "Bob"; // 有效,obj.name 的值變為 "Bob"
console.log(obj); // 輸出 { name: "Bob" }
const arr = [1, 2, 3];
arr.push(4); // 有效,arr 變為 [1, 2, 3, 4]
console.log(arr); // 輸出 [1, 2, 3, 4]
3.4 聲明時必須初始化
與 let 不同,const 聲明的變量在聲明時必須立即初始化,不能延遲初始化。
const x; // 報錯:Missing initializer in const declaration
x = 10; // 這行代碼不會執行,因為前面已經報錯
四、區別
4.1 作用域
|
特性
|
|
|
|
|
作用域 |
函數作用域或全局作用域
|
塊級作用域
|
塊級作用域
|
var:具有函數作用域或全局作用域。在塊級作用域(如if或for塊)中聲明的變量,其作用域會提升到函數或全局作用域。let和const:具有塊級作用域。變量的作用域僅限於其所在的塊(如if或for塊)。
4.2 變量提升
|
特性
|
|
|
|
|
變量提升 |
提升並初始化為 |
提升但不可用(臨時死區)
|
提升但不可用(臨時死區)
|
var:變量會被提升到其作用域的頂部,並被初始化為undefined。因此,在聲明之前訪問變量會返回undefined。let和const:變量會被提升到其作用域的頂部,但不會被初始化。在聲明之前訪問變量會拋出ReferenceError。
4.3 可變性
|
特性
|
|
|
|
|
可變性 |
可重新聲明,可重新賦值
|
不可重新聲明,可重新賦值
|
不可重新聲明,不可重新賦值
|
var:可以在同一個作用域內多次聲明同一個變量,但只有第一次聲明會生效。可以重新賦值。let:不能在同一個作用域內多次聲明同一個變量。可以重新賦值。const:不能在同一個作用域內多次聲明同一個變量。聲明後不能重新賦值,但可以修改對象或數組的屬性或元素。
4.4 初始化要求
|
特性
|
|
|
|
|
初始化 |
可延遲初始化
|
可延遲初始化
|
必須立即初始化
|
var和let:可以在聲明時延遲初始化。const:聲明時必須立即初始化,不能延遲初始化。
4.5 總結
|
特性
|
|
|
|
|
作用域 |
函數作用域或全局作用域
|
塊級作用域
|
塊級作用域
|
|
變量提升 |
提升並初始化為 |
提升但不可用(臨時死區)
|
提升但不可用(臨時死區)
|
|
可變性 |
可重新聲明,可重新賦值
|
不可重新聲明,可重新賦值
|
不可重新聲明,不可重新賦值
|
|
初始化 |
可延遲初始化
|
可延遲初始化
|
必須立即初始化
|
|
痛點 |
函數作用域、變量提升導致的意外 |
避免變量覆蓋、避免意外的 |
避免變量覆蓋、避免意外的 |