在前端處理金額或統計報表時,最常遇到的需求便是將數字加逗號與轉換千分位顯示(每三位數加上逗號
,)。若數值在顯示前需要先經過折扣或無條件進位計算,可先透過 JavaScript Math.ceil 無條件進位 處理數值,再選用以下 3 種方法轉換為千分位字串。
一、 3 種 JS 數字轉千分位方法比較表
在選擇千分位轉換方式時,可依照「是否需要傳入字串」、「是否需要指定幣別與小數位數」以及「大量資料渲染效能」來決定:
| 轉換方法 | 支援直接傳入數字字串 | 支援幣別與小數位數控制 | 適用情境 |
|---|---|---|---|
Number.prototype.toLocaleString() | 否(需先轉為 Number) | 是(透過 options 參數) | 單筆數值快速轉千分位顯示 |
Intl.NumberFormat | 是(自動轉型數值字串) | 是(內建 currency 與位數控制) | 大量列表渲染、多國幣別與小數補零 |
正規表達式(RegExp + replace) | 是(直接處理字串) | 否(需額外拆分整數與小數) | 純字串處理或特殊格式自訂替換 |
二、 分項語法拆解與程式碼範本
1. 使用 toLocaleString() 轉換千分位/加逗號
toLocaleString() 是最直觀的內建方法,但需特別注意它只對 Number 型別有效。若直接對字串 '9999999' 呼叫 String.prototype.toLocaleString(),並不會加上千分位逗號:
// 1. 傳入 Number 型別(正常加上千分位逗號)
const num = 9999999;
console.log(num.toLocaleString());
// 輸出: "9,999,999"
// 2. 傳入 String 型別(不會轉換千分位!)
const numStr = '9999999';
console.log(numStr.toLocaleString());
// 輸出: "9999999"
為了避免後端 API 傳回數字字串導致千分位失效,建議封裝一個包含 Number() 轉型與 Number.isNaN() 防呆的函式(使用 Number() 而非 parseInt(),才不會把小數點後的金額截斷):
const thousandthsFormat = (value) => {
if (value === null || value === undefined || String(value).trim() === '') {
return '';
}
const num = Number(value);
if (Number.isNaN(num)) return '';
return num.toLocaleString('en-US');
};
console.log(thousandthsFormat(9999999)); // 輸出: "9,999,999"
console.log(thousandthsFormat('9999999.5')); // 輸出: "9,999,999.5"
console.log(thousandthsFormat('abcd')); // 輸出: ""
2. 使用 Intl.NumberFormat 轉換千分位與指定幣別
Intl.NumberFormat 除了可以直接將「數字」或「純數字字串」轉換為千分位顯示外,在迴圈渲染上千筆資料時,重複使用同一個 Intl.NumberFormat 實例的效能也顯著優於重複呼叫 toLocaleString():
const formatter = new Intl.NumberFormat('en-US');
const number1 = 123456.789;
console.log(formatter.format(number1));
// 輸出: "123,456.789"
const number2 = '123456.789';
console.log(formatter.format(number2));
// 輸出: "123,456.789"
使用 Intl.NumberFormat 的另一個優勢,是能直接透過參數指定幣別符號(style: 'currency')或固定保留小數點後補零位數(minimumFractionDigits,亦可參考 JavaScript 數字後面補 0(Trailing Zero)):
const number = 123456.789;
// 1. 新臺幣 (TWD) 顯示(預設保留 2 位小數並四捨五入)
const twd = new Intl.NumberFormat('zh-TW', {
style: 'currency',
currency: 'TWD',
}).format(number);
console.log(twd); // 輸出: "$123,456.79" 或 "NT$123,456.79"
// 2. 日圓 (JPY) 顯示(日圓無小數分位,自動取整至整數)
const jpy = new Intl.NumberFormat('ja-JP', {
style: 'currency',
currency: 'JPY',
}).format(number);
console.log(jpy); // 輸出: "¥123,457"
// 3. 純千分位且強制保留小數點後 2 位(不足補 0)
const fixed2 = new Intl.NumberFormat('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
}).format(1234.5);
console.log(fixed2); // 輸出: "1,234.50"
更多國際化參數設定可參照 MDN Web Docs: Intl.NumberFormat。
3. 使用正規表達式(RegExp)轉換千分位/加逗號
若需要跨環境處理純字串,或避免超長數字(超過 16 位數 Number.MAX_SAFE_INTEGER)在轉為 Number 時發生尾數精度遺失,可採用正規表達式結合 String.prototype.replace()。
需要注意的是,若數字包含小數點超過 3 位(如 1234567.8912),直接對整串字元套用 /\B(?=(\d{3})+(?!\d))/g 會連小數點後方的數字也加上逗號。因此建議先以 .split('.') 拆開整數與小數部分,僅針對整數部分加上逗號:
const thousandthsRegexFormat = (value) => {
if (value === null || value === undefined) return '';
const parts = String(value).trim().split('.');
// 僅針對小數點前的整數部分,每 3 位數插入逗號
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',');
return parts.join('.');
};
console.log(thousandthsRegexFormat(1234567.89)); // 輸出: "1,234,567.89"
console.log(thousandthsRegexFormat(1234567.8912)); // 輸出: "1,234,567.8912"(小數點後不會誤加逗號)
三、 常見問題與排錯 Q&A
Q1:使用者在 input 輸入框填入千分位金額後,要如何還原回純數字送給後端?
- 問題原因:帶有逗號的字串(如
"1,234,567")若直接傳給Number("1,234,567")會回傳NaN;若傳給parseInt("1,234,567")則只會讀到第一個逗號前的1。 - 解決思路:在進行數學計算或送出表單前,先使用
str.replace(/,/g, '')移除所有逗號,再轉為Number(表單數字驗證可搭配 Input RegExp 表單驗證 — 只輸入數字)。
Q2:為什麼 toLocaleString() 顯示的小數位數最多只有 3 位?
- 問題原因:根據 ECMA-402 國際化規範,
toLocaleString()與Intl.NumberFormat的maximumFractionDigits預設值為3,超過第 3 位的小數會自動被四捨五入。 - 解決思路:若需保留更多位小數,可在第二個參數傳入
{ maximumFractionDigits: 6 }明確指定上限。
發佈留言