1-JavaScript forEach
方法原理解析
基本实现原理
forEach
的核心是一个高阶函数,其伪代码实现如下:
javascript
Array.prototype.forEach = function(callback, thisArg) {
// 1. 基础校验
if (this == null) {
throw new TypeError('this is null or not defined');
}
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function');
}
// 2. 获取数组数据
const O = Object(this);
const len = O.length >>> 0; // 转换为无符号32位整数
// 3. 遍历执行
let k = 0;
while (k < len) {
if (k in O) { // 检查是否存在该索引(处理稀疏数组)
callback.call(thisArg, O[k], k, O);
}
k++;
}
};
关键特性解析
1. 执行机制
-
遍历顺序:严格按索引升序执行(0 → length-1)
-
空位处理 :稀疏数组中空元素(empty)不会触发回调
javascriptconst sparse = [1,,3]; sparse.forEach((item, i) => { console.log(i, item); // 输出: 0 1 → 2 3(跳过空位) });
2. 中断控制
-
不可中断 :无法通过常规手段终止遍历(区别于
for
循环) -
变通方案 :
javascriptconst arr = [1,2,3,4,5]; arr.forEach((item) => { if (item === 3) throw new Error('break'); // 不推荐 console.log(item); }); // 更推荐使用 some/every 代替: arr.some(item => { if (item === 3) return true; // 返回true中断 console.log(item); });
3. 上下文绑定
-
可通过第二参数绑定
this
:javascriptclass Counter { constructor() { this.count = 0; } increment(arr) { arr.forEach(function(item) { this.count += item; }, this); // 绑定当前实例 } }
与普通 for 循环对比
特性 | forEach |
for 循环 |
---|---|---|
语法复杂度 | 声明式,更简洁 | 命令式,需手动控制 |
性能 | 稍慢(函数调用开销) | 更快 |
中断能力 | 不可中断 | 可 break 中断 |
异步支持 | 无法等待异步操作 | 可配合 async/await |
空元素处理 | 自动跳过 | 需要手动判断 |
底层实现示例
V8 引擎中的关键实现步骤:
- 检查数组是否被修改(写时复制保护)
- 处理稀疏数组的快速路径
- 生成优化后的机器代码(TurboFan 编译器)
最佳实践场景
适用场景
-
纯遍历操作(无返回值需求)
-
需要简洁的链式调用时
javascriptdata.filter(...) .map(...) .forEach(renderItem);
不适用场景
- 需要提前终止的遍历
- 性能敏感的密集循环
- 依赖返回值的操作(应使用
map
)
常见误区
javascript
// 错误1:试图获取返回值
const result = [1,2,3].forEach(x => x*2); // undefined
// 错误2:在异步函数中使用
async function process(array) {
array.forEach(async (item) => {
await doSomething(item); // 不会等待
});
console.log('Done'); // 会先执行
}
扩展知识
现代 JavaScript 引擎会对 forEach
做以下优化:
- 内联回调函数(Inline Caching)
- 生成类型特化代码(TurboFan)
- 跳过数组边界检查(Fast Path)
建议在性能关键路径使用 for
循环,其他场景优先考虑 forEach
的代码可读性。
2-JavaScript reduce()
方法深度解析
reduce()
是 JavaScript 数组中最强大的高阶函数之一,它通过迭代处理数组元素,最终将数组"缩减"为单个值。下面我将从多个角度详细解析它的工作原理。
基础语法
javascript
array.reduce(callback(accumulator, currentValue, index, array), initialValue)
核心参数解析
参数 | 说明 |
---|---|
accumulator |
累积器,存储每次回调的返回值(或初始值) |
currentValue |
当前处理的数组元素 |
index |
当前元素的索引(可选) |
array |
调用reduce的原始数组(可选) |
initialValue |
初始值(可选),如果不提供则使用数组第一个元素作为初始accumulator值 |
执行过程详解
1. 无初始值的情况
javascript
[1, 2, 3].reduce((acc, cur) => acc + cur)
执行步骤:
- 第一次调用:acc = 1 (数组第一个元素), cur = 2
- 第二次调用:acc = 1+2 = 3, cur = 3
- 返回最终结果:3 + 3 = 6
2. 有初始值的情况
javascript
[1, 2, 3].reduce((acc, cur) => acc + cur, 10)
执行步骤:
- 第一次调用:acc = 10 (初始值), cur = 1
- 第二次调用:acc = 10+1 = 11, cur = 2
- 第三次调用:acc = 11+2 = 13, cur = 3
- 返回最终结果:13 + 3 = 16
底层实现原理
javascript
Array.prototype.reduce = function(callback, initialValue) {
if (this == null) {
throw new TypeError('Array.prototype.reduce called on null or undefined');
}
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function');
}
const O = Object(this);
const len = O.length >>> 0;
let k = 0;
let accumulator;
// 处理初始值
if (arguments.length >= 2) {
accumulator = initialValue;
} else {
// 无初始值时,跳过空位找到第一个有效元素
while (k < len && !(k in O)) {
k++;
}
if (k >= len) {
throw new TypeError('Reduce of empty array with no initial value');
}
accumulator = O[k++];
}
// 迭代处理
while (k < len) {
if (k in O) {
accumulator = callback.call(undefined, accumulator, O[k], k, O);
}
k++;
}
return accumulator;
};
高级应用场景
1. 多维数组扁平化
javascript
const nested = [[1, 2], [3, 4], [5, 6]];
const flat = nested.reduce((acc, cur) => acc.concat(cur), []);
// 结果: [1, 2, 3, 4, 5, 6]
初始值: []
↓ 合并 [1,2]
[1, 2]
↓ 合并 [3,4]
[1, 2, 3, 4]
↓ 合并 [5,6]
[1, 2, 3, 4, 5, 6]
//conca方法:
const arr1 = [1, 2];
const arr2 = [3, 4];
const obj = {x: 5};
const result = arr1.concat(arr2, obj, 5);
// 结果: [1, 2, 3, 4, {x: 5}, 5]
1-与展开运算符对比
特性 | concat |
[...arr] |
---|---|---|
参数处理 | 自动展开一层数组 | 完全展开所有可迭代对象 |
非数组参数 | 直接作为元素添加 | 必须显式展开 |
性能 | 稍快(引擎优化) | 稍慢 |
可读性 | 链式调用更流畅 | 直观但嵌套复杂时难阅读 |
2-等价实现对比:
-
使用展开运算符:
ininested.reduce((acc, cur) => [...acc, ...cur], []);
-
使用 flat 方法(ES2019):
ininested.flat();
-
递归实现(处理任意嵌套):
javascriptfunction flatten(arr) { return arr.reduce((acc, cur) => acc.concat(Array.isArray(cur) ? flatten(cur) : cur), []); }
2. 按属性分组
javascript
const people = [
{name: 'Alice', age: 21},
{name: 'Bob', age: 21},
{name: 'Charlie', age: 25}
];
const grouped = people.reduce((acc, person) => {
const age = person.age;
if (!acc[age]) acc[age] = [];//检查 acc[21] 是否存在 → 不存在
acc[age].push(person);//将 Alice 对象推入数组: acc[21] = [{ name: 'Alice', age: 21 }]
return acc;
}, {});
/*
结果: {
21: [{name: 'Alice', age: 21}, {name: 'Bob', age: 21}],
25: [{name: 'Charlie', age: 25}]
}
*/
3. 函数管道组合
javascript
const pipe = (...funcs) =>
initialValue => funcs.reduce((acc, fn) => fn(acc), initialValue);
const add5 = x => x + 5;
const double = x => x * 2;
const square = x => x * x;
const transform = pipe(add5, double, square);
transform(2); // (((2 + 5) * 2) ^ 2) = 196 pipe 是从左到右执行
初始值: 2
↓ add5(2) → 7
↓ double(7) → 14
↓ square(14) → 196
性能与注意事项
- 初始值建议:总是提供初始值可以避免空数组错误
- 纯函数原则:回调函数应该是纯函数,不修改原数组
- 性能比较 :在V8引擎中,
reduce
通常比for
循环慢约30-40% - 稀疏数组 :会跳过空位(与
map
/filter
等行为一致)
常见错误
javascript
// 错误1:无初始值且空数组
[].reduce((acc, cur) => acc + cur); // TypeError
// 错误2:忘记返回accumulator
[1,2,3].reduce((acc, cur) => {
acc + cur; // 没有return语句
}, 0); // 返回undefined
// 错误3:修改原数组
[1,2,3].reduce((acc, cur, index, arr) => {
arr.pop(); // 危险!修改正在迭代的数组
return acc + cur;
}, 0);
reduce
的强大之处在于它可以将复杂的迭代逻辑抽象为简单的函数组合,是函数式编程的重要工具。理解其原理后,可以优雅地解决许多数据转换问题。
3-JavaScript 数组方法深度解析:slice 和 flatMap
slice 方法详解
核心原理
slice()
方法创建一个新数组,包含从开始到结束(不包括结束)的原数组元素的浅拷贝。
javascript
Array.prototype.slice = function(start, end) {
const result = [];
const len = this.length;
// 处理start参数
let relativeStart = start >> 0; // 转换为整数
relativeStart = relativeStart < 0 ? Math.max(len + relativeStart, 0) : Math.min(relativeStart, len);
// 处理end参数
let relativeEnd = end === undefined ? len : end >> 0;
relativeEnd = relativeEnd < 0 ? Math.max(len + relativeEnd, 0) : Math.min(relativeEnd, len);
// 拷贝元素
let count = Math.max(relativeEnd - relativeStart, 0);
let k = relativeStart;
while (count > 0) {
if (k in this) {
result.push(this[k]);
} else {
result.length++; // 处理稀疏数组
}
k++;
count--;
}
return result;
};
关键特性
-
非破坏性操作:原数组保持不变
-
浅拷贝 :对象/数组元素保持引用关系
javascriptconst objArr = [{a:1}, {b:2}]; const sliced = objArr.slice(0,1); sliced[0].a = 99; // 会修改原数组中的对象
-
参数处理 :
- 负数索引:从末尾开始计算
- 超出范围:自动截断到有效范围
- 省略end:截取到数组末尾
常见应用场景
-
数组拷贝:
javascriptconst copy = arr.slice(); // 浅拷贝整个数组
-
类数组转换:
javascriptfunction example() { const args = Array.prototype.slice.call(arguments); }
-
分页处理:
javascriptfunction getPage(items, page, pageSize) { return items.slice((page-1)*pageSize, page*pageSize); }
flatMap 方法详解
核心原理
flatMap()
首先使用映射函数映射每个元素,然后将结果压缩一层(相当于 map()
+ flat(1)
)。
javascript
Array.prototype.flatMap = function(callback, thisArg) {
return this.reduce((acc, current, index, array) => {
const result = callback.call(thisArg, current, index, array);
if (Array.isArray(result)) {
for (const item of result) {
acc.push(item);
}
} else {
acc.push(result);
}
return acc;
}, []);
};
关键特性
-
映射+扁平化:两步操作合二为一
-
只展开一层 :
javascript[1,2].flatMap(x => [[x*2]]) // [[2], [4]],不是[2,4]
-
性能优势 :比分开调用
map()
和flat()
更高效
与 map + flat 对比
javascript
// 等价实现
const result1 = arr.map(x => x.split(' ')).flat();
// flatMap实现
const result2 = arr.flatMap(x => x.split(' '));
特性 | map + flat | flatMap |
---|---|---|
性能 | 两次完整迭代 | 单次迭代 |
内存 | 生成中间数组 | 无中间数组 |
可读性 | 逻辑分开 | 更简洁 |
高级应用
-
过滤+映射组合:
javascript// 传统方式 arr.filter(x => x !== null).map(x => x * 2); // 使用flatMap arr.flatMap(x => x === null ? [] : [x * 2]);
-
多值展开:
javascriptconst data = [ {id: 1, values: [10, 20]}, {id: 2, values: [30, 40]} ]; // 展开为 [10,20,30,40] const flattened = data.flatMap(item => item.values);
-
异步数据处理:
javascriptasync function processArray(arr) { return Promise.all( arr.flatMap(async item => { const result = await processItem(item); return result.success ? [result.data] : []; }) ); }
性能优化建议
-
slice优化:
- 对于大型数组,直接使用索引循环可能更快
- 考虑使用
TypedArray
处理数值数据
-
flatMap优化:
- 避免在回调中创建大型临时数组
- 对于简单操作,
for
循环可能更高效
-
现代替代方案:
javascript// 使用展开运算符替代slice拷贝 const copy = [...arr]; // 使用Array.from处理类数组 const args = Array.from(arguments);
理解这些方法的底层原理和特性,可以帮助你在不同场景下做出最优选择。
4-JavaScript splice()
方法深度解析
splice()
是 JavaScript 数组中最强大但也最危险的方法之一,它可以直接修改原数组。让我们全面解析它的工作原理和应用场景。
核心语法
javascript
array.splice(start[, deleteCount[, item1[, item2[, ...]]])
参数详解
参数 | 描述 |
---|---|
start |
修改开始的位置(从0开始) |
deleteCount |
要移除的元素个数(可选,默认为0) |
item1, item2, ... |
要添加进数组的元素(可选) |
底层实现原理
javascript
Array.prototype._splice = function(start, deleteCount, ...items) {
const len = this.length;
// 1. 处理start参数
let relativeStart = start >> 0; // 转换为整数
relativeStart = relativeStart < 0 ? Math.max(len + relativeStart, 0) : Math.min(relativeStart, len);
// 2. 处理deleteCount参数
const actualDeleteCount = Math.min(
Math.max(deleteCount === undefined ? len : deleteCount >> 0, 0),
len - relativeStart
);
// 3. 创建被删除元素的数组
const deleted = new Array(actualDeleteCount);
for (let i = 0; i < actualDeleteCount; i++) {
const idx = relativeStart + i;
if (idx in this) {
deleted[i] = this[idx];
}
}
// 4. 计算插入和删除的元素数量差
const insertCount = items.length;
const deltaCount = insertCount - actualDeleteCount;
// 5. 调整数组长度
if (deltaCount > 0) {
// 向后移动元素腾出空间
for (let i = len - 1; i >= relativeStart + actualDeleteCount; i--) {
const from = i;
const to = i + deltaCount;
if (from in this) {
this[to] = this[from];
} else {
delete this[to];
}
}
} else if (deltaCount < 0) {
// 向前移动元素填补空缺
for (let i = relativeStart + actualDeleteCount; i < len; i++) {
const from = i;
const to = i + deltaCount;
if (from in this) {
this[to] = this[from];
} else {
delete this[to];
}
}
}
// 6. 插入新元素
for (let i = 0; i < insertCount; i++) {
this[relativeStart + i] = items[i];
}
// 7. 调整数组length属性
this.length = len + deltaCount;
return deleted;
};
执行过程示例
javascript
const arr = [1, 2, 3, 4, 5];
const removed = arr.splice(1, 2, 'a', 'b', 'c');
执行步骤分解:
- 初始数组 :
[1, 2, 3, 4, 5]
- 参数解析 :
start = 1
deleteCount = 2
- 要插入的元素:
'a', 'b', 'c'
- 删除操作 :
- 从索引1开始删除2个元素 → 删除
2
和3
removed
数组:[2, 3]
- 从索引1开始删除2个元素 → 删除
- 插入操作 :
- 在索引1处插入
'a', 'b', 'c'
- 在索引1处插入
- 结果数组 :
[1, 'a', 'b', 'c', 4, 5]
- 返回值 :
[2, 3]
(被删除的元素)
关键特性
- 破坏性操作:直接修改原数组
- 多功能性:可以同时实现删除和插入
- 返回值:总是返回被删除元素的数组
- 稀疏数组:会保留数组的稀疏性
常见使用模式
1. 删除元素
javascript
// 删除从索引2开始的3个元素
const arr = [1, 2, 3, 4, 5];
const deleted = arr.splice(2, 3);
// arr变为[1, 2], deleted为[3, 4, 5]
2. 插入元素
javascript
// 在索引1处插入元素,不删除任何元素
const arr = [1, 2, 3];
arr.splice(1, 0, 'a', 'b');
// arr变为[1, 'a', 'b', 2, 3]
3. 替换元素
javascript
// 替换索引1处的2个元素
const arr = [1, 2, 3, 4];
const deleted = arr.splice(1, 2, 'a', 'b', 'c');
// arr变为[1, 'a', 'b', 'c', 4], deleted为[2, 3]
4. 清空数组
javascript
// 快速清空数组的替代方案
const arr = [1, 2, 3];
arr.splice(0);
// arr变为[], 返回[1, 2, 3]
性能考量
-
时间复杂度:
- 删除/插入操作: O(n) - 需要移动元素
- 最坏情况: 在数组开头操作需要移动所有元素
-
优化建议:
- 批量操作优于多次小操作
- 在数组末尾操作性能最好
- 大型数组考虑使用非破坏性方法
特殊场景处理
1. 类数组对象
javascript
function example() {
const args = Array.prototype.splice.call(arguments, 0);
// 将arguments转换为真实数组
}
2. 负索引
javascript
const arr = [1, 2, 3, 4];
arr.splice(-2, 1); // 从倒数第2个位置删除1个元素
// arr变为[1, 2, 4]
3. 超出范围
javascript
const arr = [1, 2, 3];
arr.splice(5, 1, 'a'); // 开始位置超出长度,相当于在末尾添加
// arr变为[1, 2, 3, 'a']
安全替代方案
如果需要非破坏性操作,可以使用以下模式:
javascript
// 非破坏性删除
function safeDelete(arr, start, deleteCount) {
return [...arr.slice(0, start), ...arr.slice(start + deleteCount)];
}
// 非破坏性插入
function safeInsert(arr, start, ...items) {
return [...arr.slice(0, start), ...items, ...arr.slice(start)];
}
splice()
是 JavaScript 数组操作中最灵活但也最危险的方法,合理使用可以大幅简化代码,但滥用可能导致难以追踪的副作用。理解其底层原理有助于在适当场景中发挥它的最大价值。
5-JavaScript 数组增删方法快速指南
1. push()
- 末尾添加元素
作用:在数组末尾添加一个或多个元素,返回新长度
javascript
const fruits = ['apple', 'banana'];
const newLength = fruits.push('orange', 'pear');
console.log(fruits); // ['apple', 'banana', 'orange', 'pear']
console.log(newLength); // 4
2. pop()
- 末尾删除元素
作用:删除并返回数组的最后一个元素
javascript
const fruits = ['apple', 'banana', 'orange'];
const last = fruits.pop();
console.log(fruits); // ['apple', 'banana']
console.log(last); // 'orange'
3. unshift()
- 开头添加元素
作用:在数组开头添加一个或多个元素,返回新长度
javascript
const nums = [2, 3];
const newLength = nums.unshift(0, 1);
console.log(nums); // [0, 1, 2, 3]
console.log(newLength); // 4
4. shift()
- 开头删除元素
作用:删除并返回数组的第一个元素
javascript
const nums = [1, 2, 3];
const first = nums.shift();
console.log(nums); // [2, 3]
console.log(first); // 1
5. splice()
- 任意位置增删(补充)
作用:在指定位置删除/添加元素
javascript
const letters = ['a', 'b', 'c', 'd'];
// 从索引1开始删除1个元素,添加'x'和'y'
const removed = letters.splice(1, 1, 'x', 'y');
console.log(letters); // ['a', 'x', 'y', 'c', 'd']
console.log(removed); // ['b']
对比表格
方法 | 作用位置 | 修改方向 | 返回值 | 时间复杂度 |
---|---|---|---|---|
push() |
末尾 | 添加 | 新长度 | O(1) |
pop() |
末尾 | 删除 | 被删除元素 | O(1) |
unshift() |
开头 | 添加 | 新长度 | O(n) |
shift() |
开头 | 删除 | 被删除元素 | O(n) |
splice() |
任意位置 | 增删 | 被删除元素的数组 | O(n) |
使用场景示例
-
实现队列(先进先出):
javascriptconst queue = []; // 入队 queue.push('任务1'); queue.push('任务2'); // 出队 const task = queue.shift(); // '任务1'
-
实现栈(后进先出):
javascriptconst stack = []; // 入栈 stack.push('页面1'); stack.push('页面2'); // 出栈 const page = stack.pop(); // '页面2'
-
数组开头插入新数据:
javascriptconst logEntries = []; // 新日志总是放在最前面 logEntries.unshift('2023-10-01: 系统启动'); logEntries.unshift('2023-10-02: 用户登录');
这些方法都是直接修改原数组的破坏性方法,使用时需要注意对原数组的影响。
6-JavaScript 数组排序与反转方法
sort()
- 数组排序
作用:对数组元素进行排序(默认按字符串Unicode码点排序),会改变原数组
基本用法
javascript
const fruits = ['banana', 'apple', 'pear', 'orange'];
fruits.sort();
console.log(fruits); // ['apple', 'banana', 'orange', 'pear'] (字母顺序)
数字排序
javascript
const nums = [10, 5, 40, 25];
nums.sort(); // 错误方式:[10, 25, 40, 5] (按字符串排序)
nums.sort((a, b) => a - b); // 正确方式:[5, 10, 25, 40] (升序)
nums.sort((a, b) => b - a); // [40, 25, 10, 5] (降序)
对象数组排序
javascript
const users = [
{name: 'John', age: 25},
{name: 'Alice', age: 30},
{name: 'Bob', age: 20}
];
// 按年龄升序
users.sort((a, b) => a.age - b.age);
/* 结果:
[
{name: 'Bob', age: 20},
{name: 'John', age: 25},
{name: 'Alice', age: 30}
]
*/
reverse()
- 数组反转
作用:反转数组中元素的顺序,会改变原数组
基本用法
javascript
const letters = ['a', 'b', 'c', 'd'];
letters.reverse();
console.log(letters); // ['d', 'c', 'b', 'a']
结合sort使用
javascript
const nums = [1, 5, 2, 4];
nums.sort().reverse(); // 先升序排序再反转 → [5, 4, 2, 1]
看到这里,有个小点:关于 sort()
方法的排序行为解析
1. 第一个例子:[10, 5, 40, 25].sort()
javascript
const nums = [10, 5, 40, 25];
console.log(nums.sort()); // 输出: [10, 25, 40, 5]
原因:
- 当直接调用
sort()
不带比较函数时,所有元素都会被隐式转换为字符串 - 然后按照 Unicode 码点顺序排序
- 比较过程实际是:
- "10" < "25" (true)
- "25" < "40" (true)
- "40" < "5" (false,因为 "4" 的码点小于 "5")
2. 第二个例子:[1, 5, 2, 4].sort()
javascript
const nums1 = [1, 5, 2, 4];
console.log(nums1.sort()); // 输出: [1, 2, 4, 5]
看似"正确"排序的原因:
- 当数字都是个位数 时:
- 字符串表示和数值顺序一致
- "1" < "2" < "4" < "5" 与 1 < 2 < 4 < 5 结果相同
- 这其实是巧合,本质上仍然是字符串比较
关键结论
-
sort()
的默认行为:- 总是将元素转为字符串
- 按字符的 Unicode 码点排序
- 不是按数值大小排序
-
为什么看起来有时"正确":
javascript// 会"正确"排序(因为是巧合) [1, 2, 3].sort() // [1, 2, 3] // 不会正确排序(真实字符串比较) [1, 2, 10].sort() // [1, 10, 2]
-
正确的数字排序方式:
javascript// 升序 [10, 5, 40, 25].sort((a, b) => a - b); // [5, 10, 25, 40] // 降序 [10, 5, 40, 25].sort((a, b) => b - a); // [40, 25, 10, 5]
实际开发建议
-
永远不要依赖默认排序:
- 即使是 [1,2,3] 这样的简单数组
- 明确传递比较函数
-
对象数组排序:
javascriptconst users = [ {name: 'John', age: 25}, {name: 'Alice', age: 30}, {name: 'Bob', age: 20} ]; // 按年龄升序 users.sort((a, b) => a.age - b.age);
-
字符串本地化排序:
javascriptconst names = ['赵', '钱', '孙', '李']; names.sort((a, b) => a.localeCompare(b, 'zh'));
记住:sort()
的默认行为是字符串比较,数值排序必须显式提供比较函数。
对象数组反转
javascript
const items = [
{id: 1, name: 'Pen'},
{id: 2, name: 'Notebook'},
{id: 3, name: 'Pencil'}
];
items.reverse();
/* 结果:
[
{id: 3, name: 'Pencil'},
{id: 2, name: 'Notebook'},
{id: 1, name: 'Pen'}
]
*/
对比表格(补充)
方法 | 作用 | 是否修改原数组 | 返回值 | 特点 |
---|---|---|---|---|
sort() |
排序数组元素 | ✅ 是 | 排序后的数组 | 默认按字符串Unicode排序 |
reverse() |
反转数组元素顺序 | ✅ 是 | 反转后的数组 | 单纯反转,不进行排序 |
使用注意事项
-
sort()
的特殊性:-
默认排序可能不符合数字预期
-
比较函数应返回负数、0或正数:
javascriptarr.sort((a, b) => { if (a < b) return -1; // a排在b前 if (a > b) return 1; // b排在a前 return 0; // 保持顺序 });
-
-
性能考虑:
sort()
的时间复杂度通常是 O(n log n)- 大型数组排序可能影响性能
-
非破坏性替代方案:
javascript// 创建新数组排序 const sorted = [...arr].sort(); // 创建新数组反转 const reversed = [...arr].reverse();