ES6数组扩展运算符详解:函数调用、数组复制与合并

作者:木棉

时间:2026-10-23

浏览:110

本文详解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程序设计有所帮助。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章