JavaScript的常用数组API原理解析

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)不会触发回调

    javascript 复制代码
    const sparse = [1,,3];
    sparse.forEach((item, i) => {
      console.log(i, item); 
      // 输出: 0 1 → 2 3(跳过空位)
    });

2. 中断控制

  • 不可中断 :无法通过常规手段终止遍历(区别于 for 循环)

  • 变通方案

    javascript 复制代码
    const 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

    javascript 复制代码
    class Counter {
      constructor() {
        this.count = 0;
      }
      increment(arr) {
        arr.forEach(function(item) {
          this.count += item;
        }, this); // 绑定当前实例
      }
    }

与普通 for 循环对比

特性 forEach for 循环
语法复杂度 声明式,更简洁 命令式,需手动控制
性能 稍慢(函数调用开销) 更快
中断能力 不可中断 break 中断
异步支持 无法等待异步操作 可配合 async/await
空元素处理 自动跳过 需要手动判断

底层实现示例

V8 引擎中的关键实现步骤:

  1. 检查数组是否被修改(写时复制保护)
  2. 处理稀疏数组的快速路径
  3. 生成优化后的机器代码(TurboFan 编译器)

最佳实践场景

适用场景

  • 纯遍历操作(无返回值需求)

  • 需要简洁的链式调用时

    javascript 复制代码
    data.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)

执行步骤:

  1. 第一次调用:acc = 1 (数组第一个元素), cur = 2
  2. 第二次调用:acc = 1+2 = 3, cur = 3
  3. 返回最终结果:3 + 3 = 6

2. 有初始值的情况

javascript 复制代码
[1, 2, 3].reduce((acc, cur) => acc + cur, 10)

执行步骤:

  1. 第一次调用:acc = 10 (初始值), cur = 1
  2. 第二次调用:acc = 10+1 = 11, cur = 2
  3. 第三次调用:acc = 11+2 = 13, cur = 3
  4. 返回最终结果: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-等价实现对比:
  1. 使用展开运算符

    ini 复制代码
    nested.reduce((acc, cur) => [...acc, ...cur], []);
  2. 使用 flat 方法(ES2019):

    ini 复制代码
    nested.flat();
  3. 递归实现(处理任意嵌套):

    javascript 复制代码
    function 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

性能与注意事项

  1. 初始值建议:总是提供初始值可以避免空数组错误
  2. 纯函数原则:回调函数应该是纯函数,不修改原数组
  3. 性能比较 :在V8引擎中,reduce通常比for循环慢约30-40%
  4. 稀疏数组 :会跳过空位(与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;
};

关键特性

  1. 非破坏性操作:原数组保持不变

  2. 浅拷贝 :对象/数组元素保持引用关系

    javascript 复制代码
    const objArr = [{a:1}, {b:2}];
    const sliced = objArr.slice(0,1);
    sliced[0].a = 99; // 会修改原数组中的对象
  3. 参数处理

    • 负数索引:从末尾开始计算
    • 超出范围:自动截断到有效范围
    • 省略end:截取到数组末尾

常见应用场景

  1. 数组拷贝

    javascript 复制代码
    const copy = arr.slice(); // 浅拷贝整个数组
  2. 类数组转换

    javascript 复制代码
    function example() {
      const args = Array.prototype.slice.call(arguments);
    }
  3. 分页处理

    javascript 复制代码
    function 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;
  }, []);
};

关键特性

  1. 映射+扁平化:两步操作合二为一

  2. 只展开一层

    javascript 复制代码
    [1,2].flatMap(x => [[x*2]]) // [[2], [4]],不是[2,4]
  3. 性能优势 :比分开调用 map()flat() 更高效

与 map + flat 对比

javascript 复制代码
// 等价实现
const result1 = arr.map(x => x.split(' ')).flat();

// flatMap实现
const result2 = arr.flatMap(x => x.split(' '));
特性 map + flat flatMap
性能 两次完整迭代 单次迭代
内存 生成中间数组 无中间数组
可读性 逻辑分开 更简洁

高级应用

  1. 过滤+映射组合

    javascript 复制代码
    // 传统方式
    arr.filter(x => x !== null).map(x => x * 2);
    
    // 使用flatMap
    arr.flatMap(x => x === null ? [] : [x * 2]);
  2. 多值展开

    javascript 复制代码
    const data = [
      {id: 1, values: [10, 20]},
      {id: 2, values: [30, 40]}
    ];
    
    // 展开为 [10,20,30,40]
    const flattened = data.flatMap(item => item.values);
  3. 异步数据处理

    javascript 复制代码
    async function processArray(arr) {
      return Promise.all(
        arr.flatMap(async item => {
          const result = await processItem(item);
          return result.success ? [result.data] : [];
        })
      );
    }

性能优化建议

  1. slice优化

    • 对于大型数组,直接使用索引循环可能更快
    • 考虑使用 TypedArray 处理数值数据
  2. flatMap优化

    • 避免在回调中创建大型临时数组
    • 对于简单操作,for 循环可能更高效
  3. 现代替代方案

    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. 初始数组 : [1, 2, 3, 4, 5]
  2. 参数解析 :
    • start = 1
    • deleteCount = 2
    • 要插入的元素: 'a', 'b', 'c'
  3. 删除操作 :
    • 从索引1开始删除2个元素 → 删除23
    • removed 数组: [2, 3]
  4. 插入操作 :
    • 在索引1处插入'a', 'b', 'c'
  5. 结果数组 :
    • [1, 'a', 'b', 'c', 4, 5]
  6. 返回值 :
    • [2, 3] (被删除的元素)

关键特性

  1. 破坏性操作:直接修改原数组
  2. 多功能性:可以同时实现删除和插入
  3. 返回值:总是返回被删除元素的数组
  4. 稀疏数组:会保留数组的稀疏性

常见使用模式

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]

性能考量

  1. 时间复杂度

    • 删除/插入操作: O(n) - 需要移动元素
    • 最坏情况: 在数组开头操作需要移动所有元素
  2. 优化建议

    • 批量操作优于多次小操作
    • 在数组末尾操作性能最好
    • 大型数组考虑使用非破坏性方法

特殊场景处理

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)

使用场景示例

  1. 实现队列(先进先出)

    javascript 复制代码
    const queue = [];
    // 入队
    queue.push('任务1'); 
    queue.push('任务2');
    // 出队
    const task = queue.shift(); // '任务1'
  2. 实现栈(后进先出)

    javascript 复制代码
    const stack = [];
    // 入栈
    stack.push('页面1');
    stack.push('页面2');
    // 出栈
    const page = stack.pop(); // '页面2'
  3. 数组开头插入新数据

    javascript 复制代码
    const 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 结果相同
  • 这其实是巧合,本质上仍然是字符串比较

关键结论

  1. sort() 的默认行为

    • 总是将元素转为字符串
    • 按字符的 Unicode 码点排序
    • 不是按数值大小排序
  2. 为什么看起来有时"正确"

    javascript 复制代码
    // 会"正确"排序(因为是巧合)
    [1, 2, 3].sort() // [1, 2, 3]
    
    // 不会正确排序(真实字符串比较)
    [1, 2, 10].sort() // [1, 10, 2]
  3. 正确的数字排序方式

    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. 永远不要依赖默认排序

    • 即使是 [1,2,3] 这样的简单数组
    • 明确传递比较函数
  2. 对象数组排序

    javascript 复制代码
    const users = [
      {name: 'John', age: 25},
      {name: 'Alice', age: 30},
      {name: 'Bob', age: 20}
    ];
    
    // 按年龄升序
    users.sort((a, b) => a.age - b.age);
  3. 字符串本地化排序

    javascript 复制代码
    const 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() 反转数组元素顺序 ✅ 是 反转后的数组 单纯反转,不进行排序

使用注意事项

  1. sort() 的特殊性

    • 默认排序可能不符合数字预期

    • 比较函数应返回负数、0或正数:

      javascript 复制代码
      arr.sort((a, b) => {
        if (a < b) return -1; // a排在b前
        if (a > b) return 1;  // b排在a前
        return 0;             // 保持顺序
      });
  2. 性能考虑

    • sort() 的时间复杂度通常是 O(n log n)
    • 大型数组排序可能影响性能
  3. 非破坏性替代方案

    javascript 复制代码
    // 创建新数组排序
    const sorted = [...arr].sort();
    
    // 创建新数组反转
    const reversed = [...arr].reverse();
相关推荐
0509159 分钟前
测试基础笔记第四天(html)
前端·笔记·html
聪明的墨菲特i39 分钟前
React与Vue:哪个框架更适合入门?
开发语言·前端·javascript·vue.js·react.js
时光少年40 分钟前
Android 副屏录制方案
android·前端
拉不动的猪1 小时前
v2升级v3需要兼顾的几个方面
前端·javascript·面试
时光少年1 小时前
Android 局域网NIO案例实践
android·前端
半兽先生1 小时前
VueDOMPurifyHTML 防止 XSS(跨站脚本攻击) 风险
前端·xss
冴羽1 小时前
SvelteKit 最新中文文档教程(20)—— 最佳实践之性能
前端·javascript·svelte
Nuyoah.1 小时前
《Vue3学习手记2》
javascript·vue.js·学习
Jackson__1 小时前
面试官:谈一下在 ts 中你对 any 和 unknow 的理解
前端·typescript
zpjing~.~1 小时前
css 二维码始终显示在按钮的正下方,并且根据不同的屏幕分辨率自动调整位置
前端·javascript·html