ES6数组扩展运算符详解:函数调用、数组复制与合并
本文详解ES6扩展运算符(...)在数组操作中的应用。它可将数组转为逗号分隔的参数序列,用于函数调用、替代apply方法。同时支持数组浅拷贝与合并,需注意其仅复制引用。此外,扩展运算符还能处理字符串及具备Iterator接口的对象,结合解构赋值可简化数组生成逻辑。
扩展运算符概述与函数调用
本文通过具体实例解析ES6中数组扩展运算符的使用方式,供读者参考学习。
扩展运算符(spread)由三个点号(...)表示,其功能类似于rest参数的逆运算,主要作用是将一个数组转换为一系列用逗号分隔的参数序列。
console.log(...[1, 2, 3])
// 1 2 3
console.log(1, ...[2, 3, 4], 5)
// 1 2 3 4 5
[...document.querySelectorAll('div')]
该运算符最常见的应用场景是函数调用。
function push(array, ...items) {
array.push(...items)
}
function add(x, y) {
return x + y
}
const numbers = [4, 38]
add(...numbers) // 42
在示例代码中,`array.push(...items)`与`add(...numbers)`均属于函数调用语句,且都使用了扩展运算符。该运算符的核心机制是将一个数组展开,变为独立的参数序列传入函数。
扩展运算符可以与函数的正常参数混合使用,这种组合方式提供了极高的灵活性。
function f(v, w, x, y, z) {}
const args = [0, 1]
f(-1, ...args, 2, ...[3])
在扩展运算符之后,还可以放置具体的表达式。
const arr = [
...(x > 0 ? ['a'] : []),
'b'
]
若扩展运算符后跟随的是一个空数组,则不会产生任何实际效果。
[...[], 1]
需要注意的是,如果将扩展运算符放置在括号中,JavaScript引擎会将其识别为函数调用。若此时并非有效的函数调用场景,程序将抛出错误。
(...[1, 2])
// Uncaught SyntaxError: Unexpected number
console.log((...[1, 2]))
// Uncaught SyntaxError: Unexpected number
console.log(...[1, 2])
// 1 2
前两种情况会触发报错,因为扩展运算符所在的括号并未构成函数调用;而第三种情况`console.log(...[1, 2])`不会报错,因为此时确实构成了函数调用。
以下是扩展运算符取代传统apply方法的一个实际应用示例,利用Math.max方法简化求取数组最大元素的写法。
// ES5的写法
Math.max.apply(null, [14, 3, 77])
// ES6的写法
Math.max(...[14, 3, 77])
// 等同于
Math.max(14, 3, 77)
由于JavaScript原生未提供直接求取数组最大元素的函数,传统做法是套用Math.max函数,先将数组转换为参数序列再求最大值。引入扩展运算符后,可以直接使用Math.max进行计算。
另一个示例展示了如何通过push函数将一个数组的元素添加到另一个数组的尾部。
// ES5 的写法
var arr1 = [0, 1, 2]
var arr2 = [3, 4, 5]
Array.prototype.push.apply(arr1, arr2)
// ES6的写法
let arr1 = [0, 1, 2]
let arr2 = [3, 4, 5]
arr1.push(...arr2)
在ES5写法中,push方法的参数不能直接接收数组,因此需要通过apply方法进行变通调用。使用扩展运算符后,可以直接将数组作为参数传递给push方法。
以下是另一个相关示例。
// ES5
new (Date.bind.appy(Date, [null, 2015, 1, 1]))
// ES6
new Date(...[2015, 1, 1])
扩展运算符的应用场景
扩展运算符的主要应用场景
数组复制与浅拷贝机制
1)复制数组
数组属于复合数据类型,直接赋值复制时,仅复制了指向底层数据结构的指针,并未克隆出一个全新的数组。
const a1 = [1, 2]
const a2 = a1
a2[0] = 2
a1 // [2, 2]
在示例代码中,a2并非a1的克隆,而是指向同一份数据的另一个指针。因此,修改a2会直接导致a1的内容发生变化。
在ES5环境中,只能采用变通方法来复制数组。
const a1 = [1, 2]
const a2 = a1.concat()
a2[0] = 2
a1 //[1, 2]
ES6写法
const a1 = [1, 2]
// 写法1
const a2 = [...a1]
// 写法2
const [...a2] = a1
数组合并与解构赋值
2)合并数组
扩展运算符提供了一种新的数组合并写法。
const arr1 = ['a', 'b']
const arr2 = ['c']
const arr3 = ['d', 'e']
// ES5的合并数组
arr1.concat(arr2, arr3)
// ES6的合并数组
[...arr1, ...arr2, ...arr3]
不过,这两种方法均属于浅拷贝,使用时需特别注意。
const a1 = [{foo: 1}]
const a2 = [{bar: 2}]
const a3 = a1.concat(a2)
const a4 = [...a1, ...a2]
a3[0] === a1[0] // true
a4[0] === a1[0] // true
示例代码中,a3和a4是通过两种不同方法合并生成的新数组,但其成员依然引用原数组的成员。这属于浅拷贝,若修改原数组的成员,新数组也会同步反映变化。
3)与解构赋值结合
扩展运算符可以与解构赋值结合使用,从而用于生成数组。
// ES5
a = list[0], rest = list.slice(1)
// ES6
[a, ...rest] = list
以下是其他一些示例。
const [first, ...rest] = [1, 2, 3, 4, 5]
first // 1
rest // [2, 3, 4, 5]
const [first, ...rest] = []
first // undefined
rest // []
若将扩展运算符用于数组赋值,必须将其放置在参数的最后一位,否则程序会报错。
const [...butLast, last] = [1, 2, 3, 4, 5];
// 报错
const [first, ...middle, last] = [1, 2, 3, 4, 5];
// 报错
字符串处理与Iterator接口
(4)字符串
扩展运算符还可以将字符串转换为真正的数组。
[...'hello']
// [ "h", "e", "l", "l", "o" ]
对于那些未部署Iterator接口的类似数组对象,扩展运算符无法将其转换为真正的数组。
let arrayLike = {
'0': 'a',
'1': 'b',
length:2
}
// TypeError: Cannot spread non-iterable object.
let arr = [...arrayLike];
在示例代码中,arrayLike是一个类似数组的对象,但未部署Iterator接口,因此扩展运算符会报错。此时,可以改用Array.from方法将arrayLike转换为真正的数组。
扩展运算符内部调用的是数据结构的Iterator接口,因此只要具备Iterator接口的对象,均可使用扩展运算符,例如Map结构。
let map = new Map([
[1, 'one'],
[2, 'two'],
[3, 'three'],
]);
let arr = [...map.keys()]; // [1, 2, 3]
感兴趣的朋友可以使用在线HTML/CSS/JavaScript代码运行工具:http://tools.jb51.net/code/HtmlJsRun测试上述代码运行效果。
更多关于JavaScript相关内容可查看本站专题:《JavaScript常用函数技巧汇总》、《javascript面向对象入门教程》、《JavaScript错误与调试技巧总结》、《JavaScript数据结构与算法技巧总结》及《JavaScript数学运算用法总结》
希望本文所述对大家JavaScript程序设计有所帮助。
JS中将数组和对象混合组装为JSON字符串
2026-11-05 16:41:42
JS运算符优先级与结合性的区别是什么?
2026-11-05 13:22:02
JS Map怎么只遍历Key或Value
2026-11-05 13:00:36
D3.js 与 ECharts 选型对比:灵活性 vs 开发效率
2026-11-05 09:45:01
为什么JS中typeof []返回object:数组类型检测误区解析
2026-11-04 19:50:47
