在 JavaScript 中,varletconst 都可以用來聲明變量,但它們在作用域、提升行為和可變性等方面存在顯著差異。


一、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

let

const

作用域

函數作用域或全局作用域

塊級作用域

塊級作用域

  • var:具有函數作用域或全局作用域。在塊級作用域(如 iffor 塊)中聲明的變量,其作用域會提升到函數或全局作用域。
  • letconst:具有塊級作用域。變量的作用域僅限於其所在的塊(如 iffor 塊)。

4.2 變量提升

特性

var

let

const

變量提升

提升並初始化為 undefined

提升但不可用(臨時死區)

提升但不可用(臨時死區)

  • var:變量會被提升到其作用域的頂部,並被初始化為 undefined。因此,在聲明之前訪問變量會返回 undefined
  • letconst:變量會被提升到其作用域的頂部,但不會被初始化。在聲明之前訪問變量會拋出 ReferenceError

4.3 可變性

特性

var

let

const

可變性

可重新聲明,可重新賦值

不可重新聲明,可重新賦值

不可重新聲明,不可重新賦值

  • var:可以在同一個作用域內多次聲明同一個變量,但只有第一次聲明會生效。可以重新賦值。
  • let:不能在同一個作用域內多次聲明同一個變量。可以重新賦值。
  • const:不能在同一個作用域內多次聲明同一個變量。聲明後不能重新賦值,但可以修改對象或數組的屬性或元素。

4.4 初始化要求

特性

var

let

const

初始化

可延遲初始化

可延遲初始化

必須立即初始化

  • varlet:可以在聲明時延遲初始化。
  • const:聲明時必須立即初始化,不能延遲初始化。

4.5 總結

特性

var

let

const

作用域

函數作用域或全局作用域

塊級作用域

塊級作用域

變量提升

提升並初始化為 undefined

提升但不可用(臨時死區)

提升但不可用(臨時死區)

可變性

可重新聲明,可重新賦值

不可重新聲明,可重新賦值

不可重新聲明,不可重新賦值

初始化

可延遲初始化

可延遲初始化

必須立即初始化

痛點

函數作用域、變量提升導致的意外 undefined、可重新聲明導致的變量覆蓋、全局變量污染、閉包中的變量捕獲問題

避免變量覆蓋、避免意外的 undefined

避免變量覆蓋、避免意外的 undefined、聲明不可變的常量