JavaScript数组常用方法清单:增删改查与遍历API详解
全面梳理 JavaScript 数组常用 API 清单,围绕增、删、改、查与遍历聚合五大核心维度深入解析其参数、返回值、原地变异特性、时间复杂度与工程最佳实践。
一、增:向数组中添加元素
向数组追加数据有两种工程思路:一种是在原内存空间直接扩容追加(性能开销低,但产生副作用),另一种是生成一个包含新项的新数组实例(符合不可变数据流规范)。
1. push() 与 unshift():首尾原地插入
push() 接收一个或多个参数,依次追加到数组末尾;unshift() 则将元素插入到数组索引为 0 的头部位置。二者的共同点是都会直接改变原数组,且返回值均为添加后的新数组长度(length),而不是被修改后的数组本身。
// 示例:末尾与首部添加
const list = ['b', 'c'];
// 1. push 尾部追加
const pushLen = list.push('d', 'e');
console.log('push 返回值(新长度):', pushLen);
console.log('原数组变为:', list);
// 2. unshift 首部插入
const unshiftLen = list.unshift('a');
console.log('unshift 返回值(新长度):', unshiftLen);
console.log('原数组最终状态:', list);
原数组变为: ['b', 'c', 'd', 'e']
unshift 返回值(新长度): 5
原数组最终状态: ['a', 'b', 'c', 'd', 'e']
push() 在 V8 引擎中只需调整 length 指针并在尾部写入内存,时间复杂度为 O(1);而 unshift() 插入头部时,引擎必须将后续所有元素依次向右挪移一位重排索引,时间复杂度为 O(n)。在处理上万条级别的数据集合时,应尽量避免频繁调用 unshift()。
2. concat() 与展开运算符 [...]:不可变拼接追加
当需要向数组追加元素且不能破坏原有数据引用时,首选 ES6 展开运算符(Spread Operator)或 concat()。二者都会返回一个浅拷贝的新数组,原数组保持不变。
const base = [1, 2];
const addedByConcat = base.concat(3, [4, 5]);
const addedBySpread = [...base, 3, 4, 5];
console.log('base 原数组:', base);
console.log('addedByConcat:', addedByConcat);
console.log('addedBySpread:', addedBySpread);
console.log('引用是否相同:', base === addedBySpread);
addedByConcat: [1, 2, 3, 4, 5]
addedBySpread: [1, 2, 3, 4, 5]
引用是否相同: false
二、删:从数组中移除元素
从数组中删除元素同样分为单端弹出、区间切除与按条件过滤三种模式。
1. pop() 与 shift():首尾单项弹出
pop() 移除并返回数组末尾的最后一个元素;shift() 移除并返回数组首部的第一个元素。当对空数组调用 pop() 或 shift() 时,操作安全且返回 undefined,不会抛出异常。
const fruits = ['apple', 'banana', 'orange'];
const last = fruits.pop();
console.log('pop 弹出的元素:', last); // 'orange'
const first = fruits.shift();
console.log('shift 弹出的元素:', first); // 'apple'
console.log('剩余数组元素:', fruits); // ['banana']
shift 弹出的元素: apple
剩余数组元素: ['banana']
2. splice() vs slice():原地切除与纯函数截取
这两个方法名称极为相近,是新手极易混淆的难点:
splice(startIndex, deleteCount, ...items):原地变异方法。从startIndex开始移除deleteCount个元素,并在该位置插入可选的items。其返回值是被删除元素组成的新数组。slice(startIndex, endIndex):纯函数非变异方法。截取区间[startIndex, endIndex)(左闭右开),返回截取到的新数组,原数组完全不受影响。
// 对比 splice 与 slice
const arr1 = ['zero', 'one', 'two', 'three'];
const removed = arr1.splice(1, 2); // 从下标1开始删2项
console.log('arr1 splice 后:', arr1);
console.log('splice 返回值:', removed);
const arr2 = ['zero', 'one', 'two', 'three'];
const sub = arr2.slice(1, 3); // 截取下标 1 到 2
console.log('arr2 slice 后:', arr2);
console.log('slice 返回值:', sub);
splice 返回值: ['one', 'two']
arr2 slice 后: ['zero', 'one', 'two', 'three']
slice 返回值: ['one', 'two']
3. filter():声明式按条件删除
在工程业务逻辑中,“删除满足某条件的项”通常直接采用 filter() 表达。它遍历数组,把回调函数返回 true 的元素保留在新数组中,达到反向过滤剔除目标元素的目的。
const users = [
{ id: 1, name: 'Alice', active: true },
{ id: 2, name: 'Bob', active: false },
{ id: 3, name: 'Charlie', active: true }
];
// 剔除所有未激活用户(保留 active === true)
const activeUsers = users.filter(user => user.active);
console.log('激活用户数:', activeUsers.length);
console.log('原用户列表不受破坏:', users.length);
原用户列表不受破坏: 3
arr = [] 仅改变了当前变量的引用,若其他对象仍持有旧数组引用,则无法同步释放;而执行 arr.length = 0 会直接清空该堆内存中的所有元素,持有同引用的所有变量均能同步感知清空。
三、改:原地替换、排序与现代不可变更新
JavaScript 传统的数组修改 API 大量采用原地修改(In-Place)模式,而在现代响应式状态管理(如 React、Vue 3 Pinia、Redux)中,原地修改常常导致视图无法察觉状态变更。为此 ECMAScript 2023 引入了一组无副作用的孪生方法。
1. sort() 与 reverse() 的排序陷阱
sort() 会原地对数组元素排序,并返回原数组的引用。极度危险的默认行为是:若不传比较函数,它会将所有元素转换为字符串并按 Unicode 码点字典序排序。例如数字 [10, 2, 5] 默认排序后为 [10, 2, 5](因为字符 '1' 比 '2' 靠前),这在业务代码中极易产生严重逻辑缺陷。
const numbers = [10, 5, 40, 25, 1000, 1];
// 错误示范:默认排序(按字符 Unicode 码点)
const wrongSorted = [...numbers].sort();
console.log('默认排序结果(错误):', wrongSorted);
// 正确示范:传递数值比较函数 (a - b 为升序,b - a 为降序)
const correctlySorted = [...numbers].sort((a, b) => a - b);
console.log('数值升序结果(正确):', correctlySorted);
数值升序结果(正确): [1, 5, 10, 25, 40, 1000]
2. ES2023 现代不可变修改方法:toSorted, toReversed, toSpliced, with
在没有 ES2023 之前,为了安全排序或反转,开发者必须手动写出 [...arr].sort()。现代标准提供了完全等价且语义清晰的纯函数替代方案:
toSorted(compareFn):等价于拷贝后sort(),返回新数组。toReversed():等价于拷贝后reverse(),返回新数组。toSpliced(start, deleteCount, ...items):非破坏性的splice,返回修改后的全新数组。with(index, value):安全修改指定下标的值,返回修改后的新数组。
const original = ['Vue', 'React', 'Angular'];
// 使用 with 替换索引 2 的元素
const updated = original.with(2, 'Svelte');
console.log('原数组未受改动:', original);
console.log('新数组替换成功:', updated);
// 使用 toReversed 倒序
const reversed = original.toReversed();
console.log('倒序新数组:', reversed);
新数组替换成功: ['Vue', 'React', 'Svelte']
倒序新数组: ['Angular', 'React', 'Vue']
3. map()、flat() 与 flatMap():形态重构与拍平
map() 是函数式编程的核心,对数组中的每个元素执行转换映射,返回长度严格相同的新数组。如果映射的结果嵌套了子数组,可以使用 flat(depth) 进行扁平化,或者直接使用单步完成映射与拍平的 flatMap()。
const orders = [
{ orderId: 101, items: ['book', 'pen'] },
{ orderId: 102, items: ['ruler'] }
];
// flatMap 提取所有单品名称并拍平成一维数组
const allProducts = orders.flatMap(order => order.items);
console.log('拍平后的单品集合:', allProducts);
四、查:高效定位、断言与检索
在查询数组时,根据所持有的条件是“原始标量值”还是“动态逻辑谓词”,以及期望的输出是“是否存在”、“所在索引”还是“元素对象”,应当精确挑选对应 API。
1. includes() vs indexOf():存在性判定的底层差异
如果只需要验证数组中是否包含某项并返回布尔值,首选 includes()。早期代码常用的 arr.indexOf(item) !== -1 存在一个经典隐患:indexOf 使用严格全等运算符(===),而 JavaScript 中 NaN === NaN 恒为 false,导致 indexOf(NaN) 永远返回 -1。includes() 采用 SameValueZero 比较算法,能够准确判定 NaN 的存在。
const values = [1, NaN, 3];
console.log('indexOf 查找 NaN:', values.indexOf(NaN)); // -1 (无法找到)
console.log('includes 查找 NaN:', values.includes(NaN)); // true (正确识别)
includes 查找 NaN: true
2. find()、findIndex() 与 findLast():回调断言查找
针对复杂对象集合,通过传递断言回调函数即可进行精细查找。与 filter() 扫描全部元素不同,find() 和 findIndex() 一旦找到首个满足条件的元素就会立即短路退出遍历,在大规模数据检索时性能显著更高。ES2023 补充了对应的倒序查找方法 findLast() 与 findLastIndex(),极大简化了“获取最新一条记录”的编码难度。
const logs = [
{ id: 1, action: 'login', time: '10:00' },
{ id: 2, action: 'edit', time: '10:05' },
{ id: 3, action: 'login', time: '10:12' }
];
// 获取最后一次登录日志
const latestLogin = logs.findLast(item => item.action === 'login');
console.log('最新登录记录:', latestLogin);
3. some() 与 every():集合断言
some() 只要数组中存在任意一个元素满足回调条件即返回 true 并短路;every() 则必须每一个元素都满足条件才返回 true,遇到首个 false 立即短路返回。特别注意:对空数组 [].every(...) 始终返回 true,这是数学逻辑中全称量词在空集上的真值定义(Vacuous Truth)。
4. at():优雅的相对索引定位
过去获取数组最后一项往往写成 arr[arr.length - 1],代码冗长且重复计算。ES2022 提供的 at(index) 原生支持负数索引,-1 即代表末尾最后一项,-2 代表倒数第二项,使代码语义更加清爽。
const letters = ['A', 'B', 'C', 'D'];
console.log('最后一项:', letters.at(-1)); // 'D'
console.log('倒数第二项:', letters.at(-2)); // 'C'
倒数第二项: C
五、遍历与聚合:迭代逻辑与数据汇总
遍历不仅用于产生控制台打印或视图渲染等副作用,更核心的作用是通过累加器将数组汇聚折叠为单一数据结构。
1. forEach() 与 for...of 的选择
forEach() 专门用于对数组每项执行副作用操作,它的返回值固定为 undefined。需要强调的是:forEach() 无法通过 break 或 return 提前中止循环,回调函数中的 return 仅相当于跳过当前迭代的 continue。若业务场景需要随时中断循环,应使用传统的 for...of 循环。
2. reduce() 与 reduceRight():强大的聚合折叠器
reduce(callback, initialValue) 接收累加器函数,将数组逐项归约为单一值。它可以实现求和、按字段分组、数组扁平化及管道式数据流转换等复杂计算。
// 场景:按角色将用户列表分组归类
const staff = [
{ name: 'Alice', role: 'Dev' },
{ name: 'Bob', role: 'QA' },
{ name: 'Carol', role: 'Dev' }
];
const grouped = staff.reduce((acc, member) => {
const { role, name } = member;
if (!acc[role]) {
acc[role] = [];
}
acc[role].push(name);
return acc;
}, {}); // 显式传递空对象 {} 作为 initialValue
console.log('分组结果:', grouped);
若不传
initialValue,reduce 会默认将数组的第 0 项作为累加器初始值,并从第 1 项开始执行回调。这会引发两个常见灾难:
- 如果对一个空数组
[]调用不带初始值的reduce,JavaScript 运行时将直接抛出不可恢复的TypeError: Reduce of empty array with no initial value; - 当数组元素是对象但归约目标是标量(如计算购物车总额),第 0 项对象将被直接当作累加器初值与数字相加,导致计算结果发生隐式类型转换变成
"[object Object]20"乱码。
六、数组方法核心特性与速查总结
为了在工程编码与代码审查中快速核对方法的副作用、返回值与类型,以下全景表格整理了核心常用 API 的执行特性:
| 方法名称 | 操作类别 | 变异原数组 | 返回值类型 | 典型应用场景与核心提示 |
|---|---|---|---|---|
push(...) |
增 | 是 | number |
尾部追加一个或多个元素,返回添加后的数组新长度 |
unshift(...) |
增 | 是 | number |
首部插入元素,导致全量索引右移重排,耗时 O(n) |
pop() |
删 | 是 | T | undefined |
移除并返回末尾元素;空数组返回 undefined |
shift() |
删 | 是 | T | undefined |
移除并返回首部元素;后续元素全部左移一位 |
splice(...) |
增/删/改 | 是 | Array<T> |
指定位置删除并可选插入新项;返回被删除元素组成的数组 |
slice(...) |
截取/删 | 否 | Array<T> |
纯函数浅拷贝截取,原数组完全不受改动 |
concat(...) |
增/拼接 | 否 | Array<T> |
合并多个数组或单项,返回全新数组 |
filter(...) |
删/过滤 | 否 | Array<T> |
按回调条件保留满足项生成新数组,常用于声明式删除 |
map(...) |
改/映射 | 否 | Array<U> |
一对一数据结构映射转换,长度保持严格一致 |
sort(...) |
改/排序 | 是 | Array<T> |
原地排序原数组,务必传 (a, b) => a - b 比较函数 |
toSorted(...) |
改/排序 | 否 | Array<T> |
ES2023 纯函数排序,返回排序后的新数组 |
with(idx, val) |
改/替换 | 否 | Array<T> |
ES2023 单项不可变替换,避免破坏原数组引用 |
includes(...) |
查/包含 | 否 | boolean |
值存在性检测,支持识别 NaN,优先于 indexOf |
find(...) |
查/元素 | 否 | T | undefined |
查找首个符合条件的元素,找到立即短路退出 |
findIndex(...) |
查/索引 | 否 | number |
查找符合条件的元素下标,未找到返回 -1 |
at(index) |
查/读取 | 否 | T | undefined |
ES2022 相对索引访问,支持 -1 简洁获取末项 |
reduce(...) |
聚合/折叠 | 否 | Accumulator |
数组聚合计算核心,务必始终显式传递 initialValue |
在涉及数据展示、状态管理及纯函数工具库开发时,优先选择
slice、concat、filter、map、toSorted、toSpliced 等不可变方法,保护数据源的不可预测污染;而在高性能本地计算、海量缓冲区处理等内存敏感场景中,才合理选用 push、pop、splice 等原地变异方法提升执行效率。
JS怎么判断一个数能否被另一个数整除
2026-11-12 19:30:03
React组件和普通JavaScript函数有什么区别?
2026-11-12 13:34:26
JS解析JSON字符串报错SyntaxError怎么处理
2026-11-12 09:56:59
JavaScript处理文本框浮点数运算精度丢失的解决方案
2026-11-12 09:21:16
JS数组末尾添加元素:push()方法详解与返回值陷阱
2026-11-11 15:39:57
相关标签:








