我们先来看一看MDN的官方定义
展开语法(Spread syntax), 可以在函数调用/数组构造
时, 将 数组表达式或者 string在语法层面展开;
还可以在构造字面量对象
时, 将 对象表达式按key-value的方式展开。(译者注: 字面量一般指 [1, 2, 3] 或者 {name: "mdn"} 这种简洁的构造方式)
从定义我们可以了解到展开语法的使用场景如下
-
函数
调用 -
数组
构造 - 构造
字面量对象
(ES2018)
作用如下
- 展开
数组
- 展开
字符串
- 展开
对象
(只能用于构造字面量对象)
1. 在函数调用时使用
// 展开数组
console.log(...[1,2,3]) // 1 2 3
// 展开字符串
console.log(...'hello world') // h e l l o w o r l d
// 展开对象【无法使用,会报错】
console.log(...{a:1}) // Uncaught TypeError
1.1 与rest参数对比
在函数调用时使用展开语法时,需要特别注意数组、字符串其实是被展开成了参数序列。
还记得rest参数
是将参数都都收集为数组吗?展开语法
正好是它的逆运算——将数组展开为参数
还有注意的是rest参数在函数定义时使用【处理形参】,展开语法在函数调用时使用【处理实参】
,可能有些拗口,下面看个例子大家就明白了
function test(x, y, ...params) {
// 定义一个函数时,使用的是rest参数
console.log(...params) // 调用一个函数时,使用的是rest参数
}
1.2 作为apply的语法糖
let numArr = [1, 10, 2, 234]
Math.max.apply(null, numArr)
// 完全等价于
Math.max(...numArr) // 将numArr展开为参数序列
1.3 在new的时候使用
因为new的时候是无法调用apply的,所以展开语法这个时候起到了很牛X的作用
function Person(name, age, weight) {
this.name = name
this.age = age
this.weight = weight
}
let blues = new Person('blueswong', '16', '60')
// 完全等价于
let blues = new Person(...['blueswong', '16', '60'])
这在需要生产很多个实例时,非常有用
function Person(name, age, weight) {
this.name = name
this.age = age
this.weight = weight
}
let persons = [['blues1', '16', '60'], ['blues2', '16', '60'], ['blues3', '16', '60']]
let createdPersons = {}
persons.forEach(e => {
console.log(e[0])
createdPersons[e[0]] = new Person(...e)
})
2. 在数组构造时使用
// 展开数组
let arr1 = [1, 2, 3]
let arr2 = [0, ...arr1, 4] // [0, 1, 2, 3, 4]
// 展开字符串
let arr1 = [1, 2, 3, ...'456'] // [1, 2, 3, "4", "5", "6"]
2.1 代替将已有数组元素插入到新数组重的所有API
以往我们将已有数组的 元素插入到新数组的中,需要借用一些API例如push/unshift/splice/concat
,现在我们使用展开语法可以对上述api进行替换。需要特别强调的是,这在创建新数组的时候才比较方便
let arr = [4, 5]
let arr2 = [6, 7, 8]
// 在数组任意位置增加元素
let newArr1 = [1, 2, 3, ...arr] // [1, 2, 3, 4, 5]
let newArr2 = [...arr, 1, 2, 3] // [4, 5, 1, 2, 3]
let newArr3 = [1, ...arr, 2, 3] // [1, 4, 5, 2, 3]
let newArr4 = [1, 2, ...arr, 3] // [1, 2, 4, 5, 3]
// 连接两个数组
let newArr5 = [...arr, ...arr2] // [4, 5, 6, 7, 8]
如果是对原有的数组进行操作,原有API
+在函数调用中使用展开语法
比较方便
2.2 实现对数组的浅拷贝
let obj = {a: 1}
let arr = [2, 1, '的', obj]
// 在数组任意位置增加元素
let newarr = [...arr] // [2, 1, '的', {a: 1}]
// 但仅仅是浅拷贝,新数组中的{a:1}与obj还是指向了相同的内存
newarr[3].a = 2
console.log(obj) // {a: 2}
3. 在构造字面量对象时使用
3.1 实现对象的浅拷贝
常见的实现对象浅拷贝的方法
let obj = {a: '10', b: {c: 10}, d: [1, 2, 3]}
// 1. 遍历
let newObj = {}
for (let key in obj) {
newObj[key] = obj[key]
}
console.log(newObj)
// 2.使用assign
let newObj1 = Object.assign({}, obj)
console.log(newObj1)
使用展开语法实现
let newObj2 = {...obj}
由于这是ES2018的语法,现在浏览器支持的并不到位,因此笔者就不做过多介绍了。大家可以 去mdn上查看