精通 JS 数组操作:添加、删除、替换、插入元素的全方法指南
在 JavaScript 开发中,数组是处理有序数据的核心载体,而「添加、删除、替换、插入元素」是数组最高频的操作场景。新手常陷入 "方法用错、原数组修改失控、性能低效" 的问题 ------ 比如用push替代unshift导致性能问题、用splice时参数写错引发数据错乱、分不清 "是否修改原数组" 的边界。
本文将从「场景分类」「方法拆解」「性能对比」「避坑指南」四个维度,系统讲解 JS 中数组元素增删改插的所有常用方法,结合实战案例和最佳实践,帮你精准选择合适的方法,写出高效且易维护的代码。
一、核心认知:先分清 "是否修改原数组"
在学习具体方法前,必须先明确一个核心原则:JS 数组方法分为「修改原数组」和「返回新数组」两类,这直接决定了代码的副作用和使用场景。
| 类型 | 核心特点 | 常用方法 |
|---|---|---|
| 修改原数组 | 直接操作原数组,会产生副作用 | push、pop、unshift、shift、splice、fill |
| 返回新数组 | 不修改原数组,返回新数组 | concat、slice、map、filter、扩展运算符 (...) |
关键建议:
- 若需保留原数组(如状态管理、数据溯源),优先用「返回新数组」的方法;
- 若仅需操作数据且无需保留原数组,可用「修改原数组」的方法(性能更高)。
二、添加元素:4 类方法覆盖所有场景
添加元素的核心场景分为「尾部添加」「头部添加」「指定位置添加」「批量添加」,不同场景对应不同最优方法:
1. 尾部添加(最常用)
(1)push ():修改原数组,尾部添加一个 / 多个元素
语法 :array.push(element1, ..., elementN)返回值 :添加后数组的新长度实战示例:
javascript
运行
const arr = [1, 2, 3];
const newLength = arr.push(4, 5); // 添加多个元素
console.log(arr); // [1, 2, 3, 4, 5](原数组被修改)
console.log(newLength); // 5
优势 :性能最优(数组尾部添加无索引重排),支持批量添加;适用场景:列表追加数据、栈结构(后进先出)的入栈操作。
(2)扩展运算符 (...):返回新数组,尾部添加元素
语法 :[...原数组, 新元素1, 新元素2]实战示例:
javascript
运行
const arr = [1, 2, 3];
const newArr = [...arr, 4, 5]; // 不修改原数组
console.log(arr); // [1, 2, 3](原数组不变)
console.log(newArr); // [1, 2, 3, 4, 5]
优势 :无副作用,语法简洁;适用场景:React/Vue 等框架的状态更新(需保持原状态不可变)。
2. 头部添加
(1)unshift ():修改原数组,头部添加一个 / 多个元素
语法 :array.unshift(element1, ..., elementN)返回值 :添加后数组的新长度实战示例:
javascript
运行
const arr = [3, 4, 5];
const newLength = arr.unshift(1, 2);
console.log(arr); // [1, 2, 3, 4, 5](原数组被修改)
console.log(newLength); // 5
注意 :头部添加会导致所有元素索引重排,大数据量(10 万 +)场景性能差 ;适用场景:小数据量列表的头部追加、队列结构(先进先出)的入队操作。
(2)扩展运算符 (...):返回新数组,头部添加元素
语法 :[新元素1, 新元素2, ...原数组]实战示例:
javascript
运行
const arr = [3, 4, 5];
const newArr = [1, 2, ...arr];
console.log(arr); // [3, 4, 5](原数组不变)
console.log(newArr); // [1, 2, 3, 4, 5]
优势 :相比unshift,大数据量场景性能更优(无索引重排);适用场景:不可变数据的头部添加。
3. 指定位置添加
splice ():修改原数组,任意位置添加元素
splice是数组操作的 "万能方法",支持添加、删除、替换,核心语法:语法 :array.splice(startIndex, deleteCount, item1, ..., itemN)
startIndex:起始位置(负数表示从末尾倒数,如 - 1 表示最后一个元素前);deleteCount:删除元素的数量(设为 0 则仅添加元素);item1...:要添加的元素。
实战示例(指定位置添加):
javascript
运行
const arr = [1, 2, 4, 5];
// 在索引2的位置添加元素3(deleteCount=0)
arr.splice(2, 0, 3);
console.log(arr); // [1, 2, 3, 4, 5](原数组被修改)
// 支持批量添加
arr.splice(3, 0, 3.1, 3.2);
console.log(arr); // [1, 2, 3, 3.1, 3.2, 4, 5]
适用场景:任意位置插入元素,尤其是中间位置添加。
4. 批量添加(合并数组)
concat ():返回新数组,合并多个数组 / 元素
语法 :array.concat(array1, array2, ..., elementN)实战示例:
javascript
运行
const arr1 = [1, 2];
const arr2 = [3, 4];
// 合并两个数组
const newArr1 = arr1.concat(arr2);
console.log(newArr1); // [1, 2, 3, 4]
// 合并数组+单个元素
const newArr2 = arr1.concat(arr2, 5);
console.log(newArr2); // [1, 2, 3, 4, 5]
// 原数组不变
console.log(arr1); // [1, 2]
console.log(arr2); // [3, 4]
替代方案 :扩展运算符[...arr1, ...arr2, 5](语法更简洁,推荐)。
三、删除元素:5 种方法适配不同需求
删除元素的核心场景分为「尾部删除」「头部删除」「指定位置删除」「条件删除」「清空数组」:
1. 尾部删除
pop ():修改原数组,删除最后一个元素
语法 :array.pop()返回值 :被删除的元素(数组为空则返回 undefined)实战示例:
javascript
运行
const arr = [1, 2, 3, 4, 5];
const deletedItem = arr.pop();
console.log(arr); // [1, 2, 3, 4](原数组被修改)
console.log(deletedItem); // 5
优势 :性能最优(无索引重排);适用场景:栈结构的出栈操作、列表尾部删除。
2. 头部删除
shift ():修改原数组,删除第一个元素
语法 :array.shift()返回值 :被删除的元素(数组为空则返回 undefined)实战示例:
javascript
运行
const arr = [1, 2, 3, 4, 5];
const deletedItem = arr.shift();
console.log(arr); // [2, 3, 4, 5](原数组被修改)
console.log(deletedItem); // 1
注意 :头部删除会导致索引重排,大数据量场景性能差 ;适用场景:小数据量列表的头部删除、队列结构的出队操作。
3. 指定位置删除
(1)splice ():修改原数组,删除指定位置 / 数量的元素
语法 :array.splice(startIndex, deleteCount)返回值 :包含被删除元素的数组实战示例:
javascript
运行
const arr = [1, 2, 3, 4, 5];
// 删除索引2的1个元素(元素3)
const deletedItems = arr.splice(2, 1);
console.log(arr); // [1, 2, 4, 5](原数组被修改)
console.log(deletedItems); // [3]
// 删除从索引1开始的2个元素(2、4)
arr.splice(1, 2);
console.log(arr); // [1, 5]
(2)slice ():返回新数组,"间接删除" 元素(截取保留部分)
语法 :array.slice(startIndex, endIndex)(endIndex 不包含)实战示例:
javascript
运行
const arr = [1, 2, 3, 4, 5];
// 保留索引0-1的元素(删除索引2及以后)
const newArr = arr.slice(0, 2);
console.log(arr); // [1, 2, 3, 4, 5](原数组不变)
console.log(newArr); // [1, 2]
// 保留除索引2外的所有元素(删除元素3)
const newArr2 = [...arr.slice(0, 2), ...arr.slice(3)];
console.log(newArr2); // [1, 2, 4, 5]
适用场景:不可变数据的指定位置删除(需保留原数组)。
4. 条件删除(过滤元素)
filter ():返回新数组,删除不符合条件的元素
语法 :array.filter(callback(item, index, array))回调返回值 :true保留元素,false删除元素实战示例:
javascript
运行
const arr = [1, 2, 3, 4, 5, 6];
// 删除偶数(保留奇数)
const oddArr = arr.filter(item => item % 2 !== 0);
console.log(arr); // [1, 2, 3, 4, 5, 6](原数组不变)
console.log(oddArr); // [1, 3, 5]
// 删除值为3的元素
const noThreeArr = arr.filter(item => item !== 3);
console.log(noThreeArr); // [1, 2, 4, 5, 6]
优势 :语义化强,适合按条件批量删除;适用场景:数据筛选、按规则删除元素(如删除状态为失效的列表项)。
5. 清空数组:3 种方法
javascript
运行
const arr = [1, 2, 3, 4, 5];
// 方法1:修改原数组(推荐,性能最优)
arr.length = 0;
console.log(arr); // []
// 方法2:重新赋值(原数组引用丢失)
let arr2 = [1, 2, 3];
arr2 = [];
console.log(arr2); // []
// 方法3:splice删除所有元素(修改原数组)
arr.splice(0, arr.length);
console.log(arr); // []
选型建议:
- 需保留原数组引用:用
arr.length = 0或splice; - 无需保留引用:直接赋值
[](最简洁)。
四、替换元素:精准修改指定位置的值
替换元素的核心是 "定位 + 赋值",分为「直接替换」「条件替换」「批量替换」:
1. 直接替换(已知索引)
(1)索引赋值:修改原数组,最简单的替换方式
语法 :array[index] = 新值实战示例:
javascript
运行
const arr = [1, 2, 3, 4, 5];
// 替换索引2的元素为30
arr[2] = 30;
console.log(arr); // [1, 2, 30, 4, 5]
注意 :若索引超出数组长度,会创建稀疏数组(如arr[10] = 10,索引 3-9 为 empty),需避免。
(2)splice ():修改原数组,替换指定位置元素
语法 :array.splice(startIndex, 1, 新值)(deleteCount=1 表示删除 1 个并替换)实战示例:
javascript
运行
const arr = [1, 2, 3, 4, 5];
// 替换索引2的元素为30
arr.splice(2, 1, 30);
console.log(arr); // [1, 2, 30, 4, 5]
// 批量替换(替换索引1开始的2个元素)
arr.splice(1, 2, 20, 30);
console.log(arr); // [1, 20, 30, 4, 5]
适用场景:替换 + 删除 / 添加结合(如替换的同时插入新元素)。
2. 条件替换(未知索引)
map ():返回新数组,按条件替换元素
语法 :array.map(callback(item, index, array))回调返回值 :替换后的新值(无需替换则返回原 item)实战示例:
javascript
运行
const arr = [1, 2, 3, 4, 5];
// 将值为3的元素替换为30,其余不变
const newArr = arr.map(item => item === 3 ? 30 : item);
console.log(arr); // [1, 2, 3, 4, 5](原数组不变)
console.log(newArr); // [1, 2, 30, 4, 5]
// 批量替换:所有偶数乘以2
const doubleEvenArr = arr.map(item => item % 2 === 0 ? item * 2 : item);
console.log(doubleEvenArr); // [1, 4, 3, 8, 5]
优势 :无副作用,语义化强;适用场景:不可变数据的条件替换、批量格式转换。
3. 批量替换(填充值)
fill ():修改原数组,填充指定值到数组
语法 :array.fill(value, startIndex, endIndex)
value:要填充的值;startIndex:起始位置(默认 0);endIndex:结束位置(默认数组长度,不包含)。
实战示例:
javascript
运行
const arr = [1, 2, 3, 4, 5];
// 替换所有元素为0
arr.fill(0);
console.log(arr); // [0, 0, 0, 0, 0]
// 替换索引1-3的元素为9(endIndex=4,不包含)
const arr2 = [1, 2, 3, 4, 5];
arr2.fill(9, 1, 4);
console.log(arr2); // [1, 9, 9, 9, 5]
适用场景:数组初始化、批量替换固定值。
五、插入元素:本质是 "指定位置添加"
插入元素的核心方法已在 "添加元素" 章节讲解,这里补充 2 个高频场景的最佳实践:
1. 中间位置插入(最常用)
javascript
运行
const arr = [1, 2, 4, 5];
// 在元素2和4之间插入3(索引2)
arr.splice(2, 0, 3);
console.log(arr); // [1, 2, 3, 4, 5]
2. 按条件插入(如排序插入)
javascript
运行
const arr = [1, 3, 4, 5];
const newItem = 2;
// 找到第一个大于newItem的元素索引,插入其前面
const insertIndex = arr.findIndex(item => item > newItem);
// 若所有元素都小于newItem,插入到末尾
const finalIndex = insertIndex === -1 ? arr.length : insertIndex;
arr.splice(finalIndex, 0, newItem);
console.log(arr); // [1, 2, 3, 4, 5]
六、性能对比与选型决策树
1. 性能对比(10 万条数据测试)
| 操作场景 | 最优方法 | 次优方法 | 避免使用 |
|---|---|---|---|
| 尾部添加 | push | 扩展运算符 | - |
| 头部添加 | 扩展运算符 | unshift | unshift(大数据量) |
| 指定位置删除 | splice | slice + 扩展运算符 | - |
| 条件删除 | filter | splice(循环) | 手动 for 循环删除 |
| 直接替换 | 索引赋值 | splice | map(仅单元素替换) |
2. 选型决策树
预览
查看代码
是
否
数组操作
是否需要保留原数组?
用返回新数组的方法:扩展运算符、concat、slice、filter、map
用修改原数组的方法:push、pop、unshift、shift、splice、fill
操作类型?
操作类型?
添加:扩展运算符/concat
删除:filter/slice
替换:map
尾部增删:push/pop
头部增删:unshift/shift(小数据)
指定位置操作:splice
批量替换:fill
graph TD
A[数组操作] --> B{是否需要保留原数组?}
B -->|是| C[用返回新数组的方法:扩展运算符、concat、slice、filter、map]
B -->|否| D[用修改原数组的方法:push、pop、unshift、shift、splice、fill]
C --> E{操作类型?}
D --> F{操作类型?}
E --> E1[添加:扩展运算符/concat]
E --> E2[删除:filter/slice]
E --> E3[替换:map]
F --> F1[尾部增删:push/pop]
F --> F2[头部增删:unshift/shift(小数据)]
F --> F3[指定位置操作:splice]
F --> F4[批量替换:fill]
是
否
数组操作
是否需要保留原数组?
用返回新数组的方法:扩展运算符、concat、slice、filter、map
用修改原数组的方法:push、pop、unshift、shift、splice、fill
操作类型?
操作类型?
添加:扩展运算符/concat
删除:filter/slice
替换:map
尾部增删:push/pop
头部增删:unshift/shift(小数据)
指定位置操作:splice
批量替换:fill

豆包
你的 AI 助手,助力每日工作学习
七、避坑指南(高频错误)
-
❌ 用
for循环遍历数组时删除元素,导致索引错乱:javascript
运行
// 错误示例:删除偶数会漏删(索引重排) const arr = [1, 2, 3, 4]; for (let i = 0; i < arr.length; i++) { if (arr[i] % 2 === 0) arr.splice(i, 1); } console.log(arr); // [1, 3, 4](4未被删除) // 正确示例:反向遍历 for (let i = arr.length - 1; i >= 0; i--) { if (arr[i] % 2 === 0) arr.splice(i, 1); } -
❌ 混淆
splice的参数顺序(startIndex 在前,deleteCount 在后); -
❌ 用
map做无返回值的遍历(应改用forEach); -
❌ 大数据量场景用
unshift/shift(应改用扩展运算符或链表结构)。
总结
- 数组操作的核心是区分「修改原数组」和「返回新数组」:需保留原数组用扩展运算符 /filter/map,无需保留用 push/pop/splice;
- 增删改插的最优方法:尾部用 push/pop,头部小数据用 unshift/shift(大数据用扩展运算符),指定位置用 splice,条件操作用 filter/map;
- 避坑关键:避免在正向遍历中删除元素、明确 splice 参数顺序、大数据量场景优先选择无索引重排的方法。
掌握这些方法和最佳实践,能让你在处理数组时既保证性能,又避免常见错误。记住:没有 "万能方法",只有 "最适合当前场景的方法"。