JavaScript多数组合并技巧全解析
时间:2025-08-20 21:20:55 126浏览 收藏
在JavaScript中,拼接多个数组是常见的操作。本文深入探讨了两种高效且推荐的方法:扩展运算符(...)和`concat()`方法。扩展运算符以其简洁的语法,能将多个数组展开并合并为一个新数组,同时支持插入非数组元素,且保持原数组不变。`concat()`方法则可连接两个或多个数组,并返回一个新数组,也能直接接收非数组参数作为元素添加。这两种方法均遵循不可变性原则,避免了`push()`或循环拼接修改原数组的弊端。本文还将分析这两种方法在处理空数组、非数组元素以及`null`、`undefined`时的差异,助你选择最适合场景的数组拼接方案,写出更健壮、易维护的JavaScript代码。
最直接且推荐的方式是使用扩展运算符(...)或concat()方法。1. 扩展运算符能将多个数组展开并合并为一个新数组,语法简洁且支持插入非数组元素,同时保持原数组不变;2. concat()方法可连接两个或多个数组并返回新数组,还能直接接收非数组参数将其作为元素添加。两者均不修改原数组,符合不可变性原则,而push()或循环拼接会改变原数组且代码冗长,不推荐使用。
JavaScript拼接多个数组,最直接、最推荐的方式通常是使用扩展运算符(spread operator ...
)或者数组自带的 concat()
方法。它们都能优雅地将多个数组的内容合并成一个全新的数组,而不会改变原有的数组。

解决方案
在JavaScript中,要将多个数组拼接起来,我们主要依赖两种方式,它们各有其特点,但目的都是为了生成一个包含所有元素的新数组。
1. 扩展运算符(Spread Operator ...
)

我个人最喜欢用这种方式,因为它写起来非常简洁,读起来也一目了然。扩展运算符可以将一个可迭代对象(比如数组)展开成独立的元素。当我们把它用在数组字面量内部时,就能实现数组的拼接。
const array1 = [1, 2]; const array2 = [3, 4]; const array3 = [5, 6]; // 拼接多个数组 const combinedArray = [...array1, ...array2, ...array3]; console.log(combinedArray); // 输出: [1, 2, 3, 4, 5, 6] // 也可以在拼接的同时加入新元素 const anotherCombined = [0, ...array1, 99, ...array2, 100, ...array3]; console.log(anotherCombined); // 输出: [0, 1, 2, 99, 3, 4, 100, 5, 6]
这种方式的优点在于它创建了一个全新的数组,保持了原始数组的不可变性(immutability),这在现代前端开发,尤其是在使用React、Vue等框架时,是一个非常好的实践。它也允许你在拼接过程中自由插入其他元素。

2. Array.prototype.concat()
方法
concat()
方法是数组的一个原生方法,它的作用就是连接两个或更多的数组。这个方法同样不会改变现有的数组,而是返回一个新数组。
const arrayA = ['a', 'b']; const arrayB = ['c', 'd']; const arrayC = ['e', 'f']; // 拼接多个数组 const resultArr = arrayA.concat(arrayB, arrayC); console.log(resultArr); // 输出: ["a", "b", "c", "d", "e", "f"] // concat 也可以接受非数组参数,它们会被直接添加到新数组中 const mixedResult = arrayA.concat(arrayB, 'g', arrayC, 123); console.log(mixedResult); // 输出: ["a", "b", "c", "d", "g", "e", "f", 123]
concat()
方法非常直观,特别是当你有一个基础数组,然后想把其他数组或零散的元素追加到它后面时。它能接受任意数量的参数,每个参数可以是数组,也可以是单个值。
为什么不推荐使用 push()
或 for
循环来拼接数组?
这个问题其实挺有意思的,因为它触及了编程中“效率”和“代码风格”的权衡。虽然理论上你可以用 push()
结合 for
循环,甚至更“高级”一点的 Array.prototype.push.apply()
来拼接数组,但我个人以及很多同行,通常不推荐在大多数场景下这样做。
先说 for
循环加 push()
:
const arrA = [1, 2]; const arrB = [3, 4]; const finalArr = [...arrA]; // 先复制一份 arrA for (let i = 0; i < arrB.length; i++) { finalArr.push(arrB[i]); } console.log(finalArr); // 输出: [1, 2, 3, 4]
这种方式的问题在于它太“命令式”了,代码量相对大,不够简洁。更关键的是,它通常会涉及到对某个数组的“原地修改”(mutation),比如如果你直接 arrA.push(arrB[i])
,那么 arrA
本身就被改变了。在大型应用中,这种副作用(side effect)会让代码变得难以追踪和维护,尤其是在异步操作或者多个模块共享数据时,很容易出现意想不到的bug。我总是倾向于写“纯函数”,即不修改输入参数的函数,这样代码的可预测性会大大提高。
再来看 Array.prototype.push.apply()
:
const arrX = [1, 2]; const arrY = [3, 4]; Array.prototype.push.apply(arrX, arrY); // 将 arrY 的元素逐个推入 arrX console.log(arrX); // 输出: [1, 2, 3, 4] // 注意:arrX 被修改了!
这种方式虽然简洁,但它最大的缺点是会直接修改第一个数组 (arrX
在这个例子中)。同样是副作用的问题。此外,apply
方法在处理非常大的数组时,可能会因为函数调用栈的限制而报错(“Maximum call stack size exceeded”),虽然这在日常开发中不常见,但总归是一个潜在的风险。
所以,总的来说,concat()
和扩展运算符更符合现代JavaScript的编程范式:它们是声明式的,更易读,而且最重要的是,它们创建新数组,保持了原始数据的不可变性。这让我们的代码更健壮,更易于理解和调试。
拼接大量数组时,性能会有差异吗?
这确实是个值得思考的问题,尤其是在处理大数据量或者对性能有极致要求的场景下。不过,对于我们日常绝大多数的Web开发任务来说,concat()
和扩展运算符在性能上的差异几乎可以忽略不计。
从底层实现来看,两者都需要遍历所有待拼接的数组元素,并将它们复制到一个新的内存空间。这意味着它们的时间复杂度大致是相同的,都与所有待拼接数组的总长度成正比。
- 扩展运算符
...
: 它的实现通常会涉及到引擎内部的一些优化,比如JIT编译器可能会对这种模式进行高度优化。在很多现代JavaScript引擎中,它的性能表现非常出色,甚至在某些情况下可能比concat()
略快,因为它可能避免了一些函数调用的开销。 Array.prototype.concat()
: 这是一个非常成熟的原生方法,也经过了高度优化。它的性能通常非常稳定可靠。
那么,什么时候性能差异会变得明显呢? 通常是当你处理极其庞大的数组(比如每个数组有数十万甚至数百万个元素),或者需要拼接成百上千个数组时,你才可能需要考虑微观层面的性能差异。在这些极端情况下,不同的JavaScript引擎(V8, SpiderMonkey等)和它们的版本可能会有不同的表现。
但说句实话,在大部分业务场景下,如果你的数组拼接成了性能瓶颈,那么问题可能不在于你选择了 concat
还是 ...
,而在于你的数据结构设计或者算法本身是不是存在更深层次的效率问题。例如,是不是有办法在数据生成阶段就避免多次拼接,或者有没有其他更适合处理大数据流的方案(比如使用迭代器、生成器,或者更底层的数据结构)。
我的建议是:优先选择代码的可读性和维护性。 扩展运算符通常在这方面表现更优,因为它看起来更“自然”,更符合我们对数组合并的直观理解。只有在通过性能分析工具(如浏览器开发者工具的Performance面板)确认数组拼接确实是瓶颈时,再去考虑优化它的具体实现方式。过早的性能优化,往往会牺牲代码的清晰度,而带来的收益却微乎其微。
拼接数组时,如何处理非数组元素或空数组?
在实际开发中,我们拼接的数组可能不是那么“干净”,里面可能混杂着空数组、null
、undefined
,甚至是一些非数组的原始值。理解 concat()
和扩展运算符如何处理这些情况,能帮助我们写出更健壮的代码。
1. 处理空数组
无论是 concat()
还是扩展运算符,它们处理空数组都非常优雅,并不会引入任何问题。
const arr1 = [1, 2]; const emptyArr = []; const arr3 = [3, 4]; // 使用 concat() const resultConcat = arr1.concat(emptyArr, arr3); console.log(resultConcat); // 输出: [1, 2, 3, 4] // 使用扩展运算符 const resultSpread = [...arr1, ...emptyArr, ...arr3]; console.log(resultSpread); // 输出: [1, 2, 3, 4]
它们都只是简单地跳过空数组,就像它们不存在一样,最终结果中不会有任何多余的元素。这非常符合我们的预期。
2. 处理非数组元素(如数字、字符串、对象等)
这里 concat()
和扩展运算符的行为就有所不同了,理解这个差异很重要。
concat()
方法: 如果concat()
的参数不是数组,它会直接将这些非数组参数作为单个元素添加到新数组的末尾。const baseArr = [1, 2]; const nonArrayResult = baseArr.concat(3, 'hello', { key: 'value' }, [4, 5]); console.log(nonArrayResult); // 输出: [1, 2, 3, "hello", { key: "value" }, 4, 5]
这种行为在某些场景下非常方便,比如你想在一个数组后面追加几个零散的值。
扩展运算符
...
: 扩展运算符只能用于可迭代对象(Iterable),比如数组、字符串、Set、Map等。如果你尝试直接扩展一个非可迭代对象(如数字、布尔值、null
或undefined
),它会抛出TypeError
。const arrA = [1, 2]; // 错误示例:直接扩展一个数字会报错 // const errorSpread = [...arrA, ...3]; // TypeError: 3 is not iterable // 但你可以将非数组元素直接放在数组字面量中 const correctSpread = [...arrA, 3, 'hello', { key: 'value' }]; console.log(correctSpread); // 输出: [1, 2, 3, "hello", { key: "value" }]
所以,如果你想在用扩展运算符拼接数组时加入非数组元素,你需要把这些非数组元素直接写在新的数组字面量里,而不是尝试去扩展它们。
3. 处理 null
或 undefined
concat()
方法:concat()
会将null
和undefined
视为普通的单个值,并将其添加到新数组中。const arrWithNull = [1, 2].concat(null, [3, undefined]); console.log(arrWithNull); // 输出: [1, 2, null, 3, undefined]
扩展运算符
...
: 尝试直接扩展null
或undefined
会抛出TypeError
,因为它们不是可迭代的。// const errorNullSpread = [...null]; // TypeError: null is not iterable // const errorUndefinedSpread = [...undefined]; // TypeError: undefined is not iterable // 但如果它们是数组的一部分,那没问题 const arrWithNullInIt = [1, null, 2]; const arrWithUndefinedInIt = [3, undefined, 4]; const validSpread = [...arrWithNullInIt, ...arrWithUndefinedInIt]; console.log(validSpread); // 输出: [1, null, 2, 3, undefined, 4]
总结来说,concat()
在处理混合类型的参数时更为“宽容”,它会尽可能地将所有参数都添加到结果数组中。而扩展运算符则更“严格”,它只接受可迭代对象进行扩展,非可迭代对象必须作为普通元素直接写入数组字面量。了解这些细微的差别,可以帮助我们避免一些运行时错误,并根据具体需求选择最合适的拼接方法。
好了,本文到此结束,带大家了解了《JavaScript多数组合并技巧全解析》,希望本文对你有所帮助!关注golang学习网公众号,给大家分享更多文章知识!
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
501 收藏
-
103 收藏
-
325 收藏
-
499 收藏
-
247 收藏
-
175 收藏
-
313 收藏
-
411 收藏
-
411 收藏
-
119 收藏
-
349 收藏
-
430 收藏
-
265 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 511次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 498次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 484次学习