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(){} |
參考型別 |
註:
Number、String、Boolean皆有對應的 建構子函式(Number()、String()、Boolean()),可用於顯式型別轉換。
2. 隱式型別轉換(Implicit Coercion)
JavaScript 會在運算子需要特定型別時自動把值轉換。最常見的情況包括:
| 運算子 | 轉換規則 | 範例 |
|---|---|---|
+(加號) |
若任一操作數是 字串,則全部轉成字串並串接 | 1 + "2" → "12" |
-, *, /, % |
皆會把操作數轉成 Number | "5" - 2 → 3 |
==(寬鬆相等) |
會嘗試把兩邊轉成相同型別再比較 | 0 == false → true |
!、&&、` |
` |
範例 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, undefined 為 false |
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 與原始值的相互轉換
物件(尤其是 Date、Array、RegExp)在需要字串或數字時,會呼叫內建的 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 === NaN 為 false,需要 Number.isNaN() 判斷。 |
使用 Number.isNaN(value) 或 isNaN()(注意後者會先 coercion)。 |
parseInt 的基數遺漏 |
不指定基數時,某些字串會被當成八進位或十六進位。 | 永遠提供第二個參數(radix),例如 parseInt(str, 10)。 |
null 與 undefined 的算術運算 |
兩者在加減乘除時會被轉成 0,但相等比較卻不相等(null == undefined 為 true,null === undefined 為 false)。 |
明確檢查 value == null(同時捕捉兩者)或使用 === 釐清意圖。 |
Boolean 的隱晦行為 |
Boolean("0") 為 true,但在某些語言中會被視為 false。 |
依需求自行判斷,如 value !== "" && value !== "0"。 |
最佳實踐清單
- 預設使用嚴格相等
===,避免隱式 coercion 帶來的錯誤。 - 在算術運算前顯式轉型,尤其是從表單或 API 取得的字串。
- 使用
Number.isNaN判斷NaN,而非=== NaN。 - 給
parseInt指定基數,防止不同執行環境產生差異。 - 盡量避免混用
+於加法與字串串接,可使用模板字串(`${a} + ${b}`)或String()明確轉型。 - 利用
Object.prototype.valueOf或toString了解自訂物件在 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 的核心特性之一,既能帶來語法的彈性,也可能埋下隱蔽的錯誤。掌握以下要點,才能在開發中游刃有餘:
- 了解隱式 coercion 的規則,尤其是
+、算術運算子與寬鬆相等==。 - 優先使用顯式轉型(
Number()、String()、Boolean()、parseInt()、parseFloat()),避免不必要的自動轉換。 - 嚴格相等
===與Object.prototype.valueOf/toString能讓程式碼更可預測。 - 遵守最佳實踐:指定
radix、使用Number.isNaN、在表單/API 場景中先行型別清理。 - 在實務情境(表單驗證、API 處理、日期比較、客製化類別)中,適時運用型別轉換,讓資料流通更順暢。
只要在寫程式時保持「先思考、後轉型」的習慣,型別轉換就不會再是阻礙開發的絆腳石,而會成為提升程式可讀性與可靠性的有力工具。祝你在 JavaScript 的旅程中,玩轉型別、寫出更健全的程式碼!