在JavaScript开发中,数组排序是处理数据时的高频操作。无论是简单的数字排序、字符串排序,还是复杂的对象数组按指定属性进行多条件排序,都可以通过对应方法实现。掌握JS数组排序的不同实现方式,能够大幅提升数据处理效率,也有助于开发者理解排序背后的比较逻辑。

一、JS内置sort方法基础用法
JavaScript数组原型对象上提供了sort()方法,这是实现数组排序最常用的方式。该方法会直接修改原数组,并返回排序后的数组。也就是说,调用sort()之后,原数组的顺序已经发生变化。如果业务逻辑仍然需要使用排序前的数组,必须在排序前完成拷贝。
当sort()不传入任何参数时,数组元素会先被转换为字符串,然后按照UTF-16字符编码的顺序进行比较。这种默认规则在处理英文字符串时通常符合预期,但对于数字来说,很容易产生不符合直觉的结果。例如数字10与数字2进行比较时,字符串形式的"10"会排在"2"之前,因为字符"1"的编码值小于字符"2"。
默认排序示例
下面的代码展示了默认排序在数字数组和字符串数组上的不同表现。数字数组并没有按照数值大小进行排序,而是按照字符串规则排列。
// 数字数组的默认排序 let numArr = [10, 2, 5, 1, 20]; numArr.sort(); console.log(numArr); // 输出 [1, 10, 2, 20, 5] // 字符串数组的默认排序 let strArr = ['banana', 'apple', 'cherry']; strArr.sort(); console.log(strArr); // 输出 ['apple', 'banana', 'cherry']
二、自定义排序规则实现
为了实现符合业务预期的排序结果,需要给sort()方法传入一个比较函数。比较函数接收两个参数,通常命名为a和b,分别代表正在参与比较的两个元素。函数的返回值决定了这两个元素在最终数组中的相对顺序。
比较函数的规则可以概括为:如果返回值小于0,那么a会排在b的前面;如果返回值等于0,那么a和b的相对位置保持不变;如果返回值大于0,那么b会排在a的前面。基于这一规则,开发者可以灵活实现数字、字符串以及对象数组等多种排序需求。
数字数组的升序与降序
对数字数组进行排序时,最简单的做法是直接返回两个值的差。升序排序使用a - b,降序排序使用b - a。这样可以避免默认字符串转换带来的错误顺序,使数字按照真实的数值大小进行排列。
let numArr = [10, 2, 5, 1, 20]; // 数字升序排序 numArr.sort((a, b) => a - b); console.log(numArr); // 输出 [1, 2, 5, 10, 20] // 数字降序排序 numArr.sort((a, b) => b - a); console.log(numArr); // 输出 [20, 10, 5, 2, 1]
对象数组按属性排序
实际开发中,经常需要对对象数组按照某个属性进行排序,例如按照用户年龄、商品价格或姓名进行排列。比较函数内部需要访问对象的对应属性,然后根据属性类型选择合适的比较方式。
对于数值属性,可以直接返回两个属性值的差。对于字符串属性,则需要通过大小比较来返回-1、1或0。也可以使用localeCompare()方法处理字符串排序,尤其是涉及本地化排序规则时更推荐使用。
let userArr = [
{ name: '张三', age: 25 },
{ name: '李四', age: 18 },
{ name: '王五', age: 30 }
];
// 按年龄升序排序
userArr.sort((a, b) => a.age - b.age);
console.log(userArr);
// 输出 [
// { name: '李四', age: 18 },
// { name: '张三', age: 25 },
// { name: '王五', age: 30 }
// ]
// 按姓名升序排序
userArr.sort((a, b) => {
if (a.name < b.name) return -1;
if (a.name > b.name) return 1;
return 0;
});
console.log(userArr);
// 输出 [
// { name: '李四', age: 18 },
// { name: '王五', age: 30 },
// { name: '张三', age: 25 }
// ]
多条件组合排序
更复杂的排序需求往往涉及多个字段。例如优先按照状态排序,当状态相同时再按照另一个字段排序。这种需求可以在比较函数中先判断主字段是否相等,如果主字段不同,直接返回主字段的比较结果;如果主字段相同,再继续比较次要字段。
下面示例对一个任务数组进行排序:优先按任务状态升序排列,状态相同的再按任务编号升序排列。这样可以得到稳定且符合业务规则的排序结果。
let taskArr = [
{ id: 3, status: 2, taskNo: 3 },
{ id: 1, status: 1, taskNo: 2 },
{ id: 2, status: 1, taskNo: 1 }
];
// 优先按status升序,status相同时按taskNo升序
taskArr.sort((a, b) => {
if (a.status !== b.status) {
return a.status - b.status;
}
return a.taskNo - b.taskNo;
});
console.log(taskArr);
// 输出 [
// { id: 2, status: 1, taskNo: 1 },
// { id: 1, status: 1, taskNo: 2 },
// { id: 3, status: 2, taskNo: 3 }
// ]
三、排序注意事项
使用sort()方法时,首先需要关注它会直接修改原数组这一特性。如果排序之后还需要使用原始数组,应当先对数组进行浅拷贝,再对副本执行排序。常见的拷贝方式包括使用slice()方法或扩展运算符。
比较函数会在排序过程中被多次调用,因此应尽量保持其逻辑简单。如果比较函数中包含了复杂的计算、DOM操作或外部数据访问,在数组长度较大时可能造成明显的性能损耗。通常比较函数只负责完成基础比较并返回结果即可。
对于空数组调用sort()不会抛出异常,而是直接返回空数组。对于只包含一个元素的数组,排序操作也不会产生实际变化。这些边界情况在编写通用工具函数时应当考虑进去。
let arr = [3, 1, 2]; // 先拷贝再排序,保留原始数组 let sortedArr = arr.slice().sort((a, b) => a - b); console.log(arr); // 输出 [3, 1, 2] console.log(sortedArr); // 输出 [1, 2, 3] let emptyArr = []; console.log(emptyArr.sort()); // 输出 []
四、非sort方法的排序实现
除了使用内置的sort()方法,JavaScript中还可以自行实现冒泡排序、插入排序、快速排序等经典排序算法。理解这些基础算法有助于加深对排序过程的认知,也可以帮助开发者在特定场景下实现自定义控制。
不过在实际生产环境中,更推荐优先使用内置的sort()方法。现代JavaScript引擎通常会对内置方法进行深度优化,使它在大多数数据规模下都具备良好的性能表现。手写排序算法如果处理不当,可能会出现稳定性和性能方面的问题。
下面以冒泡排序为例,展示非内置排序算法的基本思路。冒泡排序通过相邻元素的两两比较和交换,使较大的元素逐步移动到数组末尾。每一轮排序都会减少一次需要比较的范围,最终得到有序数组。
function bubbleSort(arr) {
let len = arr.length;
// 外层循环控制排序轮数
for (let i = 0; i < len - 1; i++) {
// 内层循环控制每轮比较次数
for (let j = 0; j < len - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
// 交换两个元素的位置
let temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
return arr;
}
let testArr = [5, 3, 8, 1];
console.log(bubbleSort(testArr)); // 输出 [1, 3, 5, 8]
总结而言,JavaScript数组排序的核心在于理解sort()方法的比较函数机制。掌握默认排序与自定义排序之间的差异,能够帮助开发者在处理数字、字符串以及对象数组时避免常见错误。同时,注意原数组修改、性能以及边界情况,可以让排序逻辑更加健壮。对于绝大多数场景,优先使用内置sort()方法是更务实的选择。
JavaScriptJS数组排序sort方法自定义排序数组操作修改时间:2026-07-16 19:45:31