本文 AI 產出,尚未審核

JavaScript 變數宣告:varletconst


簡介

在 JavaScript 中,變數是程式的基本構件,它負責保存資料、控制流程、以及在不同函式之間傳遞資訊。從最早的 var 到 ES6 引入的 letconst,變數的宣告方式不斷演進,目的是讓程式碼更安全、更易讀,也更符合開發者的直覺。

對於初學者而言,了解三者的差異不僅能避免常見的錯誤,還能在實務開發中寫出 可維護、可預測 的程式。本文將從語法、作用域、提升(hoisting)以及實務最佳實踐等層面,深入說明 varletconst 的使用方式,並提供多個實用範例,幫助你在日常開發中正確選擇變數宣告方式。


核心概念

1. var – 傳統的變數宣告

特性 說明
作用域 函式作用域(function scope),若在函式外宣告則為全域作用域
提升 (hoisting) 變數會被提升至所在作用域的最上方,但只提升宣告,不會提升賦值
可重新賦值 / 可重新宣告 同一作用域內可多次宣告同名變數,且隨時可改變值

範例 1:var 的提升與作用域

console.log(message); // => undefined(被提升但未賦值)

var message = 'Hello, world!';

function demo() {
  console.log(count); // => undefined(同樣被提升)
  var count = 10;
}
demo();

註解var message 在執行前已被提升,但賦值仍在原本位置,故第一次 console.log 只會得到 undefined。同理,函式內的 var count 只在 demo 函式內有效。

範例 2:全域汙染

function setGlobal() {
  // 沒有使用 var/let/const,直接賦值會自動成為全域變數
  accidental = 42;
}
setGlobal();

console.log(accidental); // => 42(全域變數)

警告:在嚴格模式 ('use strict') 下,未宣告直接賦值會拋出 ReferenceError,但在非嚴格模式下會產生隱式全域變數,容易造成 全域汙染


2. let – 块級作用域(block scope)

let 是 ES6(ECMAScript 2015)引入的關鍵字,解決了 var 在作用域與提升上的缺陷。

特性 說明
作用域 块級作用域({} 內部),包括 ifforwhile、函式等
提升 仍會提升,但在宣告之前存取會拋出 ReferenceError(稱為「暫時性死區」Temporal Dead Zone)
可重新賦值 可以改變值,但不能重新宣告同名變數於同一作用域內

範例 3:let 的块級作用域

for (let i = 0; i < 3; i++) {
  console.log(i); // 0, 1, 2
}
console.log(i); // ReferenceError: i is not defined

說明i 僅在 for 迴圈的块級作用域內有效,迴圈結束後即不存在。

範例 4:暫時性死區 (TDZ)

{
  // console.log(x); // ReferenceError: Cannot access 'x' before initialization
  let x = 5;
  console.log(x); // 5
}

重點:即使變數已被提升,仍必須在宣告之後才能存取,避免了 var 那種「提前使用卻得到 undefined」的情況。


3. const – 常數宣告

const 同樣具備 块級作用域,但它的值一旦設定就不可再更改(不可重新賦值)。值得注意的是,const 只保證「變數指向的記憶體位址」不可變,若指向的是物件或陣列,物件的屬性仍可變更

特性 說明
作用域 块級作用域
提升 let,存在暫時性死區
不可重新賦值 必須在宣告時初始化,之後不能改變指向
物件/陣列的可變性 只保證指向不變,內容仍可變更(除非使用 Object.freeze

範例 5:const 與物件的可變性

const USER = { name: 'Alice', age: 25 };
USER.age = 26;          // ✅ 可改變屬性
// USER = { name: 'Bob' }; // TypeError: Assignment to constant variable.

範例 6:const 必須立即初始化

// const PI; // SyntaxError: Missing initializer in const declaration
const PI = 3.14159;
console.log(PI);

常見陷阱與最佳實踐

陷阱 說明 解決方案
使用 var 造成全域汙染 在大型專案中容易不小心寫成全域變數,導致衝突 盡量避免 var,改用 let/const
重新宣告同名變數 var 允許在同一作用域內多次宣告,會覆蓋先前的值 使用 let/const,編譯器會報錯提醒
忘記初始化 const const 必須在宣告時賦值,否則會拋 SyntaxError 立即賦值,或改用 let
誤以為 const 讓物件不可變 只保證指向不變,物件內容仍可被修改 若需要真正的不可變物件,使用 Object.freeze() 或 Immutable.js 等工具
TDZ(暫時性死區)誤用 在宣告之前使用 let/const 會拋 ReferenceError 確保變數在使用前已宣告,或將宣告提升至程式碼最前面

推薦的變數宣告策略

  1. 預設使用 const

    • 大多數情況下,變數不需要重新指向,使用 const 能讓程式更具可預測性。
  2. 當需要重新賦值時改用 let

    • 迴圈計數器、暫存值、或根據條件改變的變數適合使用 let
  3. 只有在必須兼容舊環境或特殊需求時使用 var

    • 例如在老舊瀏覽器或某些第三方庫中仍需要 var,此時要特別留意作用域與提升的行為。

實際應用場景

1. 表單驗證的暫存值(使用 let

在處理使用者輸入時,常需要在同一函式內多次更新暫存值:

function validateForm(data) {
  let isValid = true;          // 需要在過程中改變
  let errorMsg = '';

  if (!data.email.includes('@')) {
    isValid = false;
    errorMsg = 'Email 格式不正確';
  }

  if (data.password.length < 8) {
    isValid = false;
    errorMsg = '密碼長度至少 8 位';
  }

  return { isValid, errorMsg };
}

2. 配置常數(使用 const

全域或模組層級的設定值應該固定不變:

// config.js
export const API_ENDPOINT = 'https://api.example.com/v1';
export const TIMEOUT_MS   = 5000;
export const SUPPORTED_LANGUAGES = Object.freeze(['zh-TW', 'en', 'ja']);

3. 動態生成 UI 元素(混合使用 letconst

function renderList(items) {
  const ul = document.createElement('ul'); // 不會變更指向

  for (let i = 0; i < items.length; i++) {
    const li = document.createElement('li'); // 每次迭代都重新建立
    li.textContent = items[i];
    ul.appendChild(li);
  }

  document.body.appendChild(ul);
}

4. 舊版程式碼遷移(逐步替換 var

// 舊版
var total = 0;
for (var i = 0; i < data.length; i++) {
  total += data[i];
}
console.log(total);

// 改寫後
let total = 0;
for (let i = 0; i < data.length; i++) {
  total += data[i];
}
console.log(total);

透過上述改寫,我們消除了 i 在迴圈外仍可被存取的風險,提升了程式的可讀性與安全性。


總結

  • var:具函式作用域,會提升且允許重新宣告與重新賦值。除非有特殊相容需求,否則應盡量避免使用。
  • let:具块級作用域,提升但受暫時性死區保護,可重新賦值但不可重新宣告。適合需要在同一作用域內變更值的情況。
  • const:同 let 的块級作用域與提升機制,但必須在宣告時初始化且指向不可變。適合所有不需要重新指向的變數,讓程式更具可預測性。

在日常開發中,我們 推薦先使用 const,若確實需要變更值,再改用 let,最後僅在必須支援舊環境或特殊情境時才考慮 var。透過正確的變數宣告方式,我們不僅能降低 bug 的發生率,還能讓程式碼更易於維護與閱讀。祝你在 JavaScript 的世界裡寫出乾淨、可靠的程式!