别再被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));

更多推荐