JavaScript 陣列原生 `sort()` 方法**預設按 Unicode 碼點排序字串**,直接用於數值陣列會產生錯誤順序 (如 `[1, 2, 10, 20]` → `[1, 10, 2, 20]`)。正確做法是**傳入比較函數**,並根據資料型別選擇比較邏輯。
arr.sort((a, b) => a - b); // 數值升序
arr.sort((a, b) => b - a); // 數值降序
arr.sort((a, b) => a.localeCompare(b, undefined, {numeric: true})); // 字串自然排序
/**
* 通用陣列排序
* @param {Array} arr 目標陣列
* @param {string|Function} key 物件屬性名或自定比較函數
* @param {'asc'|'desc'} order 排序方向
* @param {'number'|'date'|'string'} type 資料型別 (可選,自動推斷)
*/
function sortArray(arr, key, order = 'asc', type) {
const direction = order === 'asc' ? 1 : -1;
const getVal = typeof key === 'function' ? key : (item) => item[key];
const compare = (a, b) => {
const valA = getVal(a);
const valB = getVal(b);
let cmp = 0;
const detectedType = type || detectType(valA, valB);
switch (detectedType) {
case 'number':
cmp = Number(valA) - Number(valB);
break;
case 'date':
cmp = new Date(valA) - new Date(valB);
break;
case 'string':
default:
cmp = String(valA).localeCompare(String(valB), undefined, {numeric: true, sensitivity: 'base'});
break;
}
return direction * cmp;
};
return [...arr].sort(compare); // 回傳新陣列,不修改原陣列
}
function detectType(a, b) {
if (typeof a === 'number' && typeof b === 'number') return 'number';
if (a instanceof Date && b instanceof Date) return 'date';
if (!isNaN(Date.parse(a)) && !isNaN(Date.parse(b))) return 'date';
return 'string';
}
// 使用範例
const users = [
{name: 'Alice', age: 25, join: '2023-01-15'},
{name: 'Bob', age: 30, join: '2022-11-03'},
{name: 'Charlie', age: 25, join: '2023-05-20'},
];
console.log(sortArray(users, 'age', 'asc', 'number'));
// [{name:'Alice',age:25,...}, {name:'Charlie',age:25,...}, {name:'Bob',age:30,...}]
console.log(sortArray(users, 'join', 'desc', 'date'));
// [{name:'Charlie',join:'2023-05-20',...}, {name:'Alice',join:'2023-01-15',...}, {name:'Bob',join:'2022-11-03',...}]
console.log(sortArray(users, 'name', 'asc'));
// 自然排序: Alice, Bob, Charlie
| 型別 | 比較函數 | 說明 |
|---|---|---|
| 數值 | (a,b) => a - b | 升序;降序 b - a。Number() 處理字串數字 |
| 日期 | (a,b) => new Date(a) - new Date(b) | 支援 ISO 8601、時間戳、Date 物件 |
| 字串 | (a,b) => a.localeCompare(b, undefined, {numeric:true}) | 自然排序 (1, 2, 10),忽略大小寫 sensitivity:'base' |
| 物件多欄位 | (a,b) => a.age - b.age || a.name.localeCompare(b.name) | 先按 age,相同再按 name |
JavaScript 陣列排序核心:**永遠提供比較函數**,根據型別選擇 `a-b`、`Date` 差值或 `localeCompare`。封裝通用 `sortArray` 工具函數可覆蓋 95% 業務場景,大數據量轉向 Web Worker + TypedArray 避免阻塞主執行緒。