本文 AI 產出,尚未審核

JavaScript 變數與資料型別

主題:型別轉換(Type Conversion / Coercion)


簡介

在 JavaScript 中,型別轉換(type conversion)是每位開發者必須熟悉的概念。語言本身會在必要時自動(隱式 coercion)或由程式設計師主動(顯式 conversion)將一個值轉成另一種資料型別。若不了解這些行為,常會出現難以偵測的錯誤,尤其在比較、算術運算或與 API 互動時。

本篇文章以 淺顯易懂 的方式說明 JavaScript 常見的型別轉換規則,提供實作範例、常見陷阱與最佳實踐,讓讀者從「初學者」逐步成長為「中級」開發者,能在實務專案中自信地處理資料型別問題。


核心概念

1. 基本資料型別回顧

型別 範例 說明
Number 42, 3.14, NaN 數值(浮點數)
String 'hello', "123" 字串
Boolean true, false 布林值
null null 空值(唯一的原始型別)
undefined undefined 未定義
Symbol Symbol('id') 唯一且不可變的值
Object {}, [], function(){} 參考型別

NumberStringBoolean 皆有對應的 建構子函式Number()String()Boolean()),可用於顯式型別轉換


2. 隱式型別轉換(Implicit Coercion)

JavaScript 會在運算子需要特定型別時自動把值轉換。最常見的情況包括:

運算子 轉換規則 範例
+(加號) 若任一操作數是 字串,則全部轉成字串並串接 1 + "2""12"
-, *, /, % 皆會把操作數轉成 Number "5" - 23
==(寬鬆相等) 會嘗試把兩邊轉成相同型別再比較 0 == falsetrue
!&&、` `

範例 1:+ 造成的字串串接

let a = 10;
let b = "20";
let result = a + b;   // 10 先被轉成字串 "10",再與 "20" 串接
console.log(result); // => "1020"

範例 2:算術運算自動轉成 Number

let x = "8";
let y = "3";
let sum = x - y;      // "-" 會把字串轉成 Number
console.log(sum);    // => 5

範例 3:寬鬆相等的驚人結果

console.log(null == undefined); // true,兩者都被視為「空」的值
console.log('' == 0);           // true,空字串會被轉成 0
console.log('0' == false);      // true,兩邊都會被轉成 0

3. 顯式型別轉換(Explicit Conversion)

為了避免隱式 coercion 帶來的意外,我們可以主動把值轉成目標型別。常用的方式有:

方法 說明 範例
String(value) 轉成字串 String(123) // "123"
Number(value) 轉成數字,無效時回傳 NaN Number("12a") // NaN
Boolean(value) 轉成布林值,只有 0, -0, NaN, "", null, undefinedfalse Boolean("") // false
parseInt(str, radix) 解析字串為整數 parseInt("0xFF", 16) // 255
parseFloat(str) 解析字串為浮點數 parseFloat("3.14px") // 3.14
toString(radix) 物件(如 Number)轉成字串,可指定進位 (255).toString(2) // "11111111"

範例 4:使用 Number() 防止字串相加

let price = "199";
let tax = "0.1";

// 直接相加會得到字串 "1990.1"
let total1 = price + tax;
console.log(total1); // "1990.1"

// 先顯式轉成 Number,再相加得到正確的數值
let total2 = Number(price) + Number(tax);
console.log(total2); // 199.1

範例 5:parseInt 與基數(radix)的重要性

let binary = "1010";

// 若省略第二個參數,瀏覽器會依據字串前綴判斷基數,舊版 IE 可能誤判
let dec1 = parseInt(binary);          // 1010 (被當成十進位)
let dec2 = parseInt(binary, 2);       // 10  (正確的二進位轉十進位)

console.log(dec1, dec2); // 1010 10

範例 6:Boolean 用於判斷值是否「有意義」

function isValidInput(value) {
  // 只要不是空字串、0、null、undefined、NaN,就算有效
  return Boolean(value);
}

console.log(isValidInput("Hello")); // true
console.log(isValidInput(""));      // false
console.log(isValidInput(0));       // false

4. Object 與原始值的相互轉換

物件(尤其是 DateArrayRegExp)在需要字串或數字時,會呼叫內建的 toString()valueOf() 方法。了解這兩個方法的差異可以避免意外的比較結果。

let d = new Date('2025-01-01');
console.log(d + 0);            // "Wed Jan 01 2025 00:00:00 GMT+0800 (台北標準時間)0"
console.log(d - 0);            // 1735660800000   <-- 轉成時間戳記 (Number)
  • + 觸發 toString() → 產生字串。
  • - 觸發 valueOf() → 產生數字(時間戳記)。

常見陷阱與最佳實踐

陷阱 說明 解決方式
隱式 + 造成字串拼接 當其中一個運算元是字串時,+ 會變成串接而非加法。 使用 Number()parseInt()parseFloat() 先轉型。
===== 的差異 == 會自動 coercion,導致不易察覺的相等結果。 永遠使用 ===(嚴格相等),除非真的需要寬鬆比較。
NaN 的比較 NaN === NaNfalse,需要 Number.isNaN() 判斷。 使用 Number.isNaN(value)isNaN()(注意後者會先 coercion)。
parseInt 的基數遺漏 不指定基數時,某些字串會被當成八進位或十六進位。 永遠提供第二個參數radix),例如 parseInt(str, 10)
nullundefined 的算術運算 兩者在加減乘除時會被轉成 0,但相等比較卻不相等(null == undefinedtruenull === undefinedfalse)。 明確檢查 value == null(同時捕捉兩者)或使用 === 釐清意圖。
Boolean 的隱晦行為 Boolean("0")true,但在某些語言中會被視為 false 依需求自行判斷,如 value !== "" && value !== "0"

最佳實踐清單

  1. 預設使用嚴格相等 ===,避免隱式 coercion 帶來的錯誤。
  2. 在算術運算前顯式轉型,尤其是從表單或 API 取得的字串。
  3. 使用 Number.isNaN 判斷 NaN,而非 === NaN
  4. parseInt 指定基數,防止不同執行環境產生差異。
  5. 盡量避免混用 + 於加法與字串串接,可使用模板字串(`${a} + ${b}`)或 String() 明確轉型。
  6. 利用 Object.prototype.valueOftoString 了解自訂物件在 coercion 時的行為,必要時自行覆寫。

實際應用場景

1. 表單資料驗證

使用者在表單中輸入的數值都是 字串。若直接送給後端或做計算,必須先轉成 Number

function calculateTotal(priceStr, qtyStr) {
  const price = Number(priceStr);
  const qty   = Number(qtyStr);

  if (Number.isNaN(price) || Number.isNaN(qty)) {
    throw new Error('價格或數量不是有效的數字');
  }

  return price * qty;
}

// 假設來自 <input id="price">、<input id="qty">
const total = calculateTotal(document.getElementById('price').value,
                             document.getElementById('qty').value);
console.log('總金額:', total);

2. API 回傳資料的型別調整

許多 REST API 會把數字以字串形式回傳(尤其是 JSON 序列化時)。前端在渲染圖表或做統計前,需要先 批次轉型

fetch('/api/sales')
  .then(res => res.json())
  .then(data => {
    // data = [{ month: "2025-01", amount: "12500" }, ...]
    const formatted = data.map(item => ({
      month: item.month,
      amount: Number(item.amount)   // 立即轉成 Number
    }));

    drawChart(formatted);
  });

3. 日期與時間的比較

比較兩個 Date 物件時,必須把它們轉成時間戳記(Number)才能正確比較大小。

const start = new Date('2025-01-01');
const end   = new Date('2025-12-31');

if (end - start > 0) {
  console.log('結束日期在開始日期之後');
}

4. 客製化物件的 coercion

在大型專案中,常會建立「金額」或「比例」的類別。若希望它們能直接參與算術運算,需要實作 valueOf()

class Money {
  constructor(amount) {
    this.amount = Number(amount);
  }
  valueOf() {
    return this.amount;   // 讓 Money 物件在算術運算時自動轉成 Number
  }
  toString() {
    return `$${this.amount.toFixed(2)}`;
  }
}

let a = new Money(120);
let b = new Money('30.5');

console.log(a + b); // 150.5 (Number)
console.log(`${a} + ${b}`); // "$120.00 + $30.50"

總結

型別轉換 是 JavaScript 的核心特性之一,既能帶來語法的彈性,也可能埋下隱蔽的錯誤。掌握以下要點,才能在開發中游刃有餘:

  1. 了解隱式 coercion 的規則,尤其是 +、算術運算子與寬鬆相等 ==
  2. 優先使用顯式轉型Number()String()Boolean()parseInt()parseFloat()),避免不必要的自動轉換。
  3. 嚴格相等 ===Object.prototype.valueOf / toString 能讓程式碼更可預測。
  4. 遵守最佳實踐:指定 radix、使用 Number.isNaN、在表單/API 場景中先行型別清理。
  5. 在實務情境(表單驗證、API 處理、日期比較、客製化類別)中,適時運用型別轉換,讓資料流通更順暢。

只要在寫程式時保持「先思考、後轉型」的習慣,型別轉換就不會再是阻礙開發的絆腳石,而會成為提升程式可讀性與可靠性的有力工具。祝你在 JavaScript 的旅程中,玩轉型別、寫出更健全的程式碼!