JS数组slice和splice有什么区别?切片与删除操作对比
深入对比 JavaScript 中 Array.prototype.slice 和 splice 的核心区别。详细解析 slice 的切片只读提取与浅拷贝机制、splice 的删除/插入/替换就地修改原理、参数体系、返回值陷阱与实战边界案例。
slice 与 splice 虽拼写相近,但分属两种截然不同的操作范式:slice(切片)是纯函数式只读提取,从原数组截取指定区间生成浅拷贝新数组,原数组绝不改变;splice(胶接/拼接)则是就地破坏性操作,在原数组上直接执行删除、插入或替换,并返回被删除元素组成的数组。一句话概括:只读截取用 slice,就地修改用 splice。
一、快速判定:两者的本质差异速览
初学者容易混淆两者,主要是因为名字拼写只差一个字母 p,且两者都可以接收数字参数来处理数组的局部区间。但从执行逻辑到底层影响,两者的行为完全相反:
| 对比维度 | slice(start, end) |
splice(start, deleteCount, ...items) |
|---|---|---|
| 中文语义 | 切片、截取(像切面包片一样取下一块) | 剪接、拼接(像胶水粘合电影胶片一样剪接) |
| 对原数组的影响 | 无副作用(Immutable):原数组长度与内容均不变 | 就地修改(Mutate):原数组直接改变,长度随操作增减 |
| 参数含义 | 两个参数代表切片边界:[起始索引, 结束索引) | 前两项为起始点与删除数量,后续可无限追加新插入项 |
| 函数返回值 | 截取提取出的浅拷贝子数组 | 被删除的元素数组(未删除时返回空数组 []) |
| 典型核心用途 | 分页截取、数组浅拷贝、不可变状态提取 | 根据索引删除特定项、插入新项、原地替换 |
二、slice() 切片提取:前闭后开与浅拷贝机制
Array.prototype.slice() 用于提取数组中的一部分,并返回一个新数组对象。其核心特征在于只读与纯函数特性。
1. 语法签名与前闭后开区间
const newArr = arr.slice([start[, end]]);
start(可选):切片开始的零基索引。若省略则默认为0;若为负数,表示从数组末尾倒数计算,即length + start。end(可选):切片结束的零基索引。提取区间不包含 end 本身(即数学上的前闭后开区间[start, end))。若省略或大于数组长度,则提取至数组末尾。
2. 典型切片代码示例
const techStack = ["Vue", "React", "Angular", "Svelte", "Solid"];
// 提取索引 1 到 3(不含 3):即第 2 项与第 3 项
const picked = techStack.slice(1, 3);
// 负数索引示例:从倒数第 2 项截取到倒数第 1 项(不含)
const tail = techStack.slice(-2, -1);
tail: [ "Svelte" ]
techStack (原数组): [ "Vue", "React", "Angular", "Svelte", "Solid" ] // 长度依然为 5,毫无改变
slice() 复制的是元素的第一层值。对于数字、字符串等原始类型,复制的是独立值;但若数组内部包含对象或嵌套数组,slice 复制的仅是对象内存指针。如果通过新数组修改了嵌套对象的属性,原数组内部的对象同样会被改变。
三、splice() 变异操作:删除、插入与替换步骤拆解
Array.prototype.splice() 是一个多功能的变异(Mutating)方法。通过灵活组合参数,它可以直接在原数组上完成删除元素、插入新元素或就地替换元素三大任务。
1. 语法签名
const removedItems = arr.splice(start[, deleteCount[, item1[, item2[, ...]]]]);
start:修改的起始位置索引。若超出数组长度,则从数组末尾开始添加;若为负数,则从末尾倒数。deleteCount(可选):要移除的数组元素个数。如果为0或负数,则不移除任何元素;如果省略或大于等于start之后的元素总数,则删除其后所有元素。...items(可选):要向数组中插入的新元素,从start索引处开始补入。如果不传,则只执行删除。
2. 三种核心操作场景实战
// 场景 1:纯删除(从索引 1 开始删除 2 个元素)
const listA = ["Apple", "Banana", "Cherry", "Date"];
const deletedA = listA.splice(1, 2);
// 场景 2:纯插入(deleteCount 设为 0,在索引 2 处注入两项)
const listB = ["Red", "Green", "Blue"];
const deletedB = listB.splice(2, 0, "Yellow", "Purple");
// 场景 3:就地替换(删除索引 1 处 1 项,并在原位置插入 "Watermelon")
const listC = ["Orange", "Melon", "Peach"];
const deletedC = listC.splice(1, 1, "Watermelon");
场景 2: deletedB = [] (未删任何项) | listB 原数组变为 = [ "Red", "Green", "Yellow", "Purple", "Blue" ]
场景 3: deletedC = [ "Melon" ] | listC 原数组变为 = [ "Orange", "Watermelon", "Peach" ]
很多开发者在需要修改数组时,会顺手写出 const updated = arr.splice(1, 1);,并误以为 updated 是处理后的新数组。实际上 splice 的返回值永远是被删除的那部分项(类型为 Array),而真正被修改后的结果保留在原数组变量 arr 本身之中。
四、技术选型决策:何时用 slice,何时用 splice?
面对业务需求,选择哪个方法取决于当前上下文对数据不可变性的要求以及具体的数据处理动作。
1. 必须使用 slice 的场景
- 前端状态管理(React、Redux、Vue 等):在 React 状态更新函数或 Redux Reducer 中,框架依赖浅比较(Object.is)判断组件是否需要重新渲染。直接使用
splice就地修改 state 会破坏状态的历史记录,且可能导致组件不触发刷新。此时应使用slice进行只读截取,或生成新数组。 - 列表分页读取:服务端或前端分页渲染列表时,通常根据当前页码与每页大小提取子集:
const pageItems = fullList.slice((page - 1) * pageSize, page * pageSize);。 - 函数式管道与纯函数:在工具库编写中,函数应当保持幂等无副作用,输入数组不应被悄悄改动。
2. 适合使用 splice 的场景
- 内部算法或局部临时数组维护:在闭包或函数内部自建的私有数组中,无需对外保证不可变性。直接使用
splice就地增删元素避免了频繁创建新数组实例带来的内存分配与垃圾回收(GC)开销。 - 基于下标精确移除某一项:例如在通过
indexOf或findIndex定位到待删除元素的下标idx后,直接arr.splice(idx, 1)是最直接高效的命令式写法。
3. 现代替代方案:ECMAScript 2023 中的 toSpliced()
如果在不可变状态下确实需要进行“删除某项”或“插入某项”,除了用 slice 配合展开运算符拼接外,现代浏览器及 Node.js 20+ 已原生支持 Array.prototype.toSpliced():
// 原生不可变胶接:返回新数组,原 items 毫发无损
const items = ["A", "B", "C"];
const updated = items.toSpliced(1, 1, "NEW");
updated (新数组): [ "A", "NEW", "C" ]
五、关键差异全景总结与避坑清单
为防止在日常开发与技术面试中混淆细节,以下矩阵图系统汇总了两者的核心属性对照:
两步速记口诀
- 看名字末尾:
slice(切下薄片),只切不管改;splice(胶接拼合),动刀又动线,原物必变样。 - 看参数第二位:
slice(start, end)第二个参数是截止索引;splice(start, count)第二个参数是删除个数。 - 看接收返回值:需要得到原数组修剪后的样子,千万不要把
splice(...)的结果直接赋值给状态变量。
有Java基础转学JavaScript,需要克服哪些思维转换难点?
2026-11-12 09:17:29
JS数组合并后修改元素影响原数组怎么办
2026-11-11 15:40:36
js函数里面的data参数在面试里常怎么考
2026-11-08 18:07:07
JavaScript禁用怎么解除在某些机型上报错怎么定位
2026-11-08 17:45:33
js函数里面的data参数新旧写法的取舍怎么判断
2026-11-08 15:36:29








