别再被sort()坑了!手把手教你搞定JavaScript数组排序(附对象数组排序实战)
别再被sort()坑了!手把手教你搞定JavaScript数组排序(附对象数组排序实战)
刚接触JavaScript时,我以为 sort() 是个简单到不需要思考的方法——直到它在我的电商项目里把"价格9元"排在了"价格100元"前面。这种看似低级的错误,几乎每个开发者都会遇到。 sort() 的默认行为就像一把双刃剑:用对了能快速解决排序需求,用错了会导致难以察觉的数据错乱。本文将带你直击 sort() 的五大常见陷阱,并通过真实业务场景演示如何用比较函数实现精准排序。
1. 为什么你的数字排序总出错?揭开sort()的默认行为
打开浏览器控制台尝试这段代码:
const prices = [9, 100, 25, 30];
prices.sort();
console.log(prices); // 输出什么?
结果会是 [100, 25, 30, 9] ——这明显不是我们想要的升序排列。 90%的初学者都会在这里踩坑 ,因为 sort() 默认将所有元素转为字符串后按Unicode码点排序。就像字典里"apple"会排在"banana"前面一样,"100"的Unicode值比"9"小。
关键记忆点:任何情况下对数字数组直接调用
sort()都是危险操作
正确做法是传入比较函数:
// 升序排列
prices.sort((a, b) => a - b);
// 降序排列
prices.sort((a, b) => b - a);
这个简单的减法操作背后有精妙的设计:
- 返回负数 → a排在b前
- 返回正数 → b排在a前
- 返回0 → 保持原顺序
2. 对象数组排序实战:从单属性到多级排序
真实业务中我们更常处理对象数组。假设有个学生成绩表:
const students = [
{ name: '李华', math: 85, english: 90 },
{ name: '张明', math: 92, english: 88 },
{ name: '王芳', math: 85, english: 95 }
];
2.1 单属性排序
按数学成绩降序:
students.sort((a, b) => b.math - a.math);
2.2 多级排序
当数学成绩相同时,用英语成绩决胜负:
students.sort((a, b) => {
// 第一优先级:数学成绩
if (b.math !== a.math) {
return b.math - a.math;
}
// 第二优先级:英语成绩
return b.english - a.english;
});
2.3 动态属性排序
封装成可复用函数:
function sortBy(arr, prop, order = 'asc') {
return arr.sort((a, b) => {
const valA = a[prop];
const valB = b[prop];
return order === 'asc' ? valA - valB : valB - valA;
});
}
3. 高级技巧:处理特殊排序场景
3.1 中文拼音排序
直接排序中文字符会出现乱序:
const names = ['王伟', '李娜', '张涛'];
names.sort(); // 可能得到乱序结果
解决方案:
names.sort((a, b) => a.localeCompare(b, 'zh'));
3.2 混合类型数据排序
当数组包含数字、字符串、null等混合类型时:
const mixed = [30, 'apple', null, 15];
mixed.sort((a, b) => {
// 处理null/undefined
if (a == null) return 1;
if (b == null) return -1;
// 处理类型不同情况
if (typeof a !== typeof b) {
return typeof a < typeof b ? -1 : 1;
}
// 同类型比较
return a < b ? -1 : 1;
});
3.3 保持排序稳定性
ES2019规范要求 sort() 保持稳定性(相同元素相对位置不变),但在旧环境可能需要polyfill:
function stableSort(arr, compare) {
return arr
.map((item, index) => ({ item, index }))
.sort((a, b) => compare(a.item, b.item) || a.index - b.index)
.map(({ item }) => item);
}
4. 性能优化:百万级数据排序策略
当处理大型数据集时,需要注意:
| 方法 | 时间复杂度 | 适用场景 |
|---|---|---|
| 原生sort() | O(n log n) | 常规数据量 |
| 索引排序 | O(n) | 已知有限值范围 |
| Web Worker并行 | 视核心数而定 | CPU密集型任务 |
内存优化技巧 :
// 避免在比较函数中创建新对象
largeArray.sort((a, b) => {
// 错误示范:每次比较都创建新Date对象
// return new Date(a.time) - new Date(b.time);
// 正确做法:预处理或直接比较时间戳
return a.timestamp - b.timestamp;
});
5. 常见业务场景解决方案
5.1 表格多列排序
实现类似Excel的点击表头排序:
function multiSort(data, sorts) {
return data.sort((a, b) => {
for (const { prop, order } of sorts) {
const compare = order === 'asc'
? a[prop] - b[prop]
: b[prop] - a[prop];
if (compare !== 0) return compare;
}
return 0;
});
}
// 使用示例
multiSort(products, [
{ prop: 'category', order: 'asc' },
{ prop: 'price', order: 'desc' }
]);
5.2 随机排序
不要用 Math.random() 直接比较:
// 错误做法:可能产生偏差
array.sort(() => Math.random() - 0.5);
// 正确做法:Fisher-Yates洗牌算法
function shuffle(array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
return array;
}
5.3 自定义排序规则
实现"VIP用户优先"等业务逻辑:
users.sort((a, b) => {
// VIP用户排前面
if (a.isVIP !== b.isVIP) {
return a.isVIP ? -1 : 1;
}
// 其次按活跃度
return b.activity - a.activity;
});
在最近的项目中,我遇到一个需要按"周一到周日"顺序排序的需求。直接用 sort() 会导致乱序,最终解决方案是:
const weekdays = ['周一', '周二', '周三', '周四', '周五', '周六', '周日'];
data.sort((a, b) => weekdays.indexOf(a.day) - weekdays.indexOf(b.day));
更多推荐

所有评论(0)