JavaScript 原生sort()方法详解
一、基本语法
javascript
// 语法 arr.sort([compareFunction]) // 返回值:排序后的原数组(原地修改) const sortedArray = arr.sort(compareFunction);二、默认行为(不使用比较函数)
1.字符串排序(默认)
javascript
const fruits = ['banana', 'apple', 'cherry', 'date']; fruits.sort(); console.log(fruits); // ['apple', 'banana', 'cherry', 'date'] const numbersAsStrings = ['10', '2', '1', '20']; numbersAsStrings.sort(); console.log(numbersAsStrings); // ['1', '10', '2', '20'] ⚠️ 按字符串排序2.数字排序问题
javascript
const numbers = [10, 2, 1, 20]; numbers.sort(); console.log(numbers); // [1, 10, 2, 20] ❌ 不是数字顺序! // 这是因为 sort() 默认将元素转换为字符串比较 // 比较过程:"10" < "2" // true(按字典序比较)三、比较函数详解
1.基本数字排序
javascript
// 升序排序 arr.sort((a, b) => a - b); // 降序排序 arr.sort((a, b) => b - a); // 完整示例 const numbers = [40, 100, 1, 5, 25, 10]; numbers.sort((a, b) => a - b); console.log(numbers); // [1, 5, 10, 25, 40, 100] numbers.sort((a, b) => b - a); console.log(numbers); // [100, 40, 25, 10, 5, 1]2.比较函数工作原理
javascript
// 比较函数应该返回: // - 负数:a 排在 b 前面 // - 零:a 和 b 相对位置不变 // - 正数:a 排在 b 后面 function compare(a, b) { if (a < b) { return -1; // a 在前 } if (a > b) { return 1; // a 在后 } return 0; // 位置不变 } // 简化版(仅适用于数字) function compareNumbers(a, b) { return a - b; // 升序 }四、V8 引擎实现原理
1.使用的算法:Timsort
javascript
// Timsort = Tim Peters' sort = 归并排序 + 插入排序优化 // 特点: // 1. 稳定排序(相等元素保持原顺序) // 2. 自适应(根据数据特征选择策略) // 3. 时间复杂度:O(n log n) // 4. 空间复杂度:O(n) // 工作原理: // 1. 寻找"run"(已经有序的片段) // 2. 小片段使用插入排序 // 3. 使用归并排序合并片段 // 4. 优化内存使用(原地归并或临时数组)2.性能特征
javascript
// 不同数据规模的性能表现 const dataSizes = [100, 1000, 10000, 100000, 1000000]; // Timsort 对以下情况特别优化: // 1. 部分有序数组 // 2. 包含重复元素的数组 // 3. 逆序数组 // 4. 随机数组 // 在 Chrome 中的实际表现: // - 小数组(< 10):插入排序 // - 中等数组:快速排序变体 // - 大数组:Timsort五、复杂对象排序
1.按对象属性排序
javascript
const users = [ { name: 'Alice', age: 30 }, { name: 'Bob', age: 25 }, { name: 'Charlie', age: 35 } ]; // 按年龄升序 users.sort((a, b) => a.age - b.age); // 按名字字母顺序 users.sort((a, b) => a.name.localeCompare(b.name)); // 多条件排序 users.sort((a, b) => { if (a.age !== b.age) { return a.age - b.age; // 先按年龄 } return a.name.localeCompare(b.name); // 年龄相同按名字 });2.自定义排序规则
javascript
const priorities = ['high', 'medium', 'low']; const tasks = [ { title: 'Task A', priority: 'medium' }, { title: 'Task B', priority: 'high' }, { title: 'Task C', priority: 'low' } ]; tasks.sort((a, b) => { return priorities.indexOf(a.priority) - priorities.indexOf(b.priority); }); // 结果:high -> medium -> low六、特殊排序需求
1.中文排序
javascript
const chineseNames = ['张三', '李四', '王五', '赵六']; // 简单排序(Unicode顺序) chineseNames.sort(); console.log(chineseNames); // Unicode 顺序 // 正确的中文拼音排序 chineseNames.sort((a, b) => a.localeCompare(b, 'zh-CN')); console.log(chineseNames); // 按拼音排序 // 支持更多选项 chineseNames.sort((a, b) => a.localeCompare(b, 'zh-CN', { sensitivity: 'base', // 忽略大小写和重音 numeric: true // 数字作为数字比较 }) );2.带单位的字符串排序
javascript
const sizes = ['10px', '2px', '100px', '50px']; sizes.sort((a, b) => { const numA = parseInt(a); const numB = parseInt(b); return numA - numB; }); console.log(sizes); // ['2px', '10px', '50px', '100px']3.日期排序
javascript
const dates = [ '2024-01-15', '2023-12-01', '2024-02-20', '2023-11-10' ]; dates.sort((a, b) => new Date(a) - new Date(b)); // 或 dates.sort((a, b) => a.localeCompare(b)); // 字符串比较也适用七、性能优化技巧
1.缓存比较结果
javascript
// 优化前(每次比较都计算) const complexObjects = [...]; // 大量复杂对象 complexObjects.sort((a, b) => calculateComplexValue(a) - calculateComplexValue(b) ); // 优化后(Schwartzian transform) complexObjects .map(obj => ({ original: obj, value: calculateComplexValue(obj) })) .sort((a, b) => a.value - b.value) .map(item => item.original);2.避免在比较函数中创建对象
javascript
// 不好:每次比较都创建新对象 arr.sort((a, b) => { const dateA = new Date(a.timestamp); const dateB = new Date(b.timestamp); return dateA - dateB; }); // 好:预先处理 arr.forEach(item => { item._sortDate = new Date(item.timestamp); }); arr.sort((a, b) => a._sortDate - b._sortDate); arr.forEach(item => delete item._sortDate);八、常见陷阱
1.原地修改问题
javascript
const original = [3, 1, 4, 1, 5]; const sorted = original.sort(); console.log(original); // [1, 1, 3, 4, 5] ❌ 原数组被修改了! console.log(sorted); // [1, 1, 3, 4, 5] 同一个数组 // 正确做法:先复制 const safeSorted = [...original].sort(); // 或 const safeSorted = original.slice().sort();2.不稳定的比较函数
javascript
// 错误的比较函数(可能引起问题) [1, 2, 3, 4, 5].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; }3.NaN 处理
javascript
const arr = [1, NaN, 3, 2, NaN, 4]; arr.sort((a, b) => a - b); console.log(arr); // [1, 2, 3, 4, NaN, NaN] // NaN 被排到最后 // 自定义 NaN 处理 arr.sort((a, b) => { if (isNaN(a) && isNaN(b)) return 0; if (isNaN(a)) return 1; // NaN 在后 if (isNaN(b)) return -1; // 非NaN在前 return a - b; });九、高级应用
1.稳定排序模拟
javascript
// 如果需要绝对稳定(旧浏览器),可以添加索引 const data = [{value: 2}, {value: 1}, {value: 2}]; data .map((item, index) => ({item, index})) .sort((a, b) => { const valueDiff = a.item.value - b.item.value; if (valueDiff !== 0) return valueDiff; return a.index - b.index; // 保持原顺序 }) .map(({item}) => item);2.多维度动态排序
javascript
function dynamicSort(properties) { return function(a, b) { for (const {property, order = 'asc'} of properties) { const aVal = a[property]; const bVal = b[property]; if (aVal < bVal) return order === 'asc' ? -1 : 1; if (aVal > bVal) return order === 'asc' ? 1 : -1; } return 0; }; } const items = [...]; items.sort(dynamicSort([ {property: 'category', order: 'asc'}, {property: 'price', order: 'desc'}, {property: 'name', order: 'asc'} ]));3.性能基准测试
javascript
function benchmarkSort(arr, sortFn, iterations = 100) { const times = []; for (let i = 0; i < iterations; i++) { const copy = [...arr]; const start = performance.now(); sortFn(copy); const end = performance.now(); times.push(end - start); } return { avg: times.reduce((a, b) => a + b) / times.length, min: Math.min(...times), max: Math.max(...times), stdDev: Math.sqrt( times.map(t => Math.pow(t - avg, 2)) .reduce((a, b) => a + b) / times.length ) }; }十、总结
最佳实践:
始终提供比较函数(除非明确需要字符串排序)
注意原地修改- 必要时先复制数组
复杂比较预计算- 提高性能
利用稳定性- 多条件排序依赖稳定排序特性
考虑数据特性- 部分有序数据 Timsort 表现更好
选择sort()当:
需要稳定排序
处理复杂对象
代码简洁性优先
数组大小适中(< 1百万)
考虑其他方案当:
需要绝对性能(TypedArray)
内存极度受限(堆排序)
特殊数据结构(如链表)
只需要部分排序(选择算法)