Array.prototype.concat()和slice()对纯基础类型数组的拷贝效果看似深拷贝,实为浅拷贝;它们仅复制第一层元素,基础类型因值传递且不可变,故不相互影响,但含对象等引用类型时会共享内存地址。

Array.prototype.concat 和 slice 对纯基础类型数组(如 [1, 2, 3]、['a', 'b'])的拷贝效果等同于深拷贝,但这只是“伪深拷贝”——本质仍是浅拷贝,只是因为基础类型值不可变且不共享引用,所以行为上看起来像深拷贝。
为什么基础类型数组用 concat/slice 看起来像深拷贝
基础类型(number、string、boolean、null、undefined、symbol、bigint)在赋值时是值传递。数组中存储的是这些值的副本,而非引用。因此,即使 concat 或 slice 只复制一层引用(即新数组的每个元素仍指向原值),由于这些值本身无法被修改,也就不存在“一个改、另一个跟着变”的问题。
-
原数组:
const a = [1, 'hello', true] -
拷贝后:
const b = a.slice()→b === [1, 'hello', true] -
修改 b 不影响 a:
b[0] = 99,此时a仍是[1, 'hello', true]
concat 和 slice 都只做一层浅拷贝
它们都创建一个新数组,但仅复制第一层元素。对基础类型来说,这一层复制就足够了;但一旦数组里出现对象、数组或函数,就会暴露浅拷贝本质。
-
[1, {x: 1}].slice()返回的新数组,其第二个元素仍和原数组引用同一个对象 -
concat同理,[1, [2]].concat()中的子数组[2]是共享引用的 - 两者都不递归拷贝嵌套结构,也无能力识别数据类型做差异化处理
“伪深拷贝”成立的前提很严格
这个行为只在满足以下全部条件时才安全可靠:
- 数组中所有元素都是基础类型(不含 object、array、function、date、regexp 等)
- 后续不会向数组中 push 引用类型值(否则破坏“纯基础”前提)
- 不依赖对象身份比较(如
===判等),因为拷贝后已是不同数组实例
不要误以为它们能替代真正的深拷贝
遇到嵌套结构或混合类型,concat/slice 会失效。例如:
❌ 错误假设:const arr = [1, {name: 'Alice'}];<br>const copy = arr.concat();<br>copy[1].name = 'Bob'; // arr[1].name 也会变成 'Bob'这时必须用 JSON.parse(JSON.stringify())(仅限可序列化数据)、structuredClone(现代环境)、或专用库如 lodash.cloneDeep。

















