concat不修改原数组,返回新数组需显式赋值;仅浅拷贝,嵌套数组引用不变;展开运算符语义更清晰、类型推导更优,但对undefined更敏感。

concat 不会修改原数组,但要注意返回值的使用方式
concat 本身是纯函数式操作,它从不改变调用它的数组,而是返回一个新数组。常见误解是以为写了 arr.concat([1,2]) 就“拼好了”,其实没赋值的话,返回值直接被丢弃了。
- 必须显式接收返回值:
const newArr = arr1.concat(arr2, arr3) - 原数组
arr1、arr2、arr3都保持完全不变 - 如果传入的是嵌套数组(如
[1, [2, 3]]),concat默认只浅拷贝一层,内部数组仍是同一引用
替代方案:用展开运算符(...)更直观且语义清晰
ES6+ 中,[...arr1, ...arr2, ...arr3] 实际效果和 concat 一致,但可读性更强,也更容易嵌套使用(比如拼接时过滤或映射)。
- 同样不修改原数组,返回全新数组
- 对空数组或
undefined更敏感:若arr2是undefined,concat会把它当字面量推入,而展开会报错;需提前校验 - 性能上,小数组无差异;超大数组(>10万项)时
concat略快,但通常不构成瓶颈
遇到对象数组时,concat 只浅拷贝,别误以为“深不可变”
很多人拼接含对象的数组后,发现改新数组里的对象属性,原数组里对应对象也变了——这是因为 concat 和展开运算符都只做浅拷贝。
- 例如:
const a = [{x: 1}]; const b = a.concat([{y: 2}]); b[0].x = 99;→a[0].x也会变成 99 - 若需深层隔离,得手动深拷贝元素,比如:
arr1.map(x => ({...x})).concat(arr2.map(x => ({...x}))) - 注意:
JSON.parse(JSON.stringify())虽然能深拷贝,但会丢失函数、undefined、Date、RegExp 等类型
TypeScript 下 concat 的类型推导有时不如展开运算符准确
在较老版本 TypeScript(如 4.5 之前)中,concat 对联合类型数组的返回类型可能退化为 any[] 或宽泛的 (string | number)[],而展开运算符通常能保留更精确的联合类型信息。
- 示例:
const a: string[] = ['a']; const b: number[] = [1]; const c = a.concat(b);——c类型可能是(string | number)[],但某些场景下推导失败 - 升级到 TS 4.7+ 后改善明显,不过遇到复杂泛型组合时,展开仍更稳定
- 若用
concat且类型丢失,可加类型断言:a.concat(b) as Array<string number></string>
filter、map)。这时候直接用展开语法配合管道式写法更自然,而硬套 concat 反而要多包一层括号。

















