认识 Spread
...
,展开运算符(如果算是运算符);- 将可迭代对象展开成参数列表(如果将很多行为看待称一个函数行为)。
基本语法
根据基本用途:
- 用作函数传参;
- 用作构造数组;
- 用作构造对象(浅拷贝)。
语法如下:
/* 用作函数传参:可迭代对象可以是数组、字符串、集合、生成器等等有迭代入口的对象 */
func(0个或更多其他参数, ...可迭代对象, 0个或更多其他参数)
/* 用作构造数组; */
[ 1, 2, ...[3, 4], 5, ...'67890' ]
[...[1, 2], ...[3, 4, 5]] // 像这样的数组拼接,也可以使用 concat 方法
/* 用作构造对象(浅拷贝):也叫属性拷贝什么的 */
let objClone = { ...obj };
一个简单例子 - 数组最值
尝试使用函数 Math.max / Math.min
求一个数组的最大/最小值:
- 直接将数组传入,数组被当成一个数据,一个非数的数据,得到结果是
NaN
; - 进一步,我们知道,使用函数的
apply
方法,可以将数组或类数组的元素作为参数列表进行传参; - 更进一步,使用展开语法,也可以将数组展开,再作为参数传给函数。
const arr = [1, 2, 4];
Math.max(arr) // NaN
Math.max.apply(null, arr) // 4
Math.max(...arr) // 4
Math.max(1, 2, 4) // 4 等价于前两种
Math.max(...'124') // 4 字符串也是可迭代对象
Spread 语法的应用
代替函数的 apply
方法
使用展开语法,将类数组展开成参数列表:
let args = [1, 2, 3, 4];
sum.apply(null, args) // 10
sum(...args) // 10
sum(...'1234') // 10
sum(1, ...[2, 3], ...[], 4, 5) // 15
function sum(...rest) {
let total = 0;
for (const elem of rest) total += +elem;
return total;
}
构造数组
- 复制数组(浅拷贝);
- 拼接数组。
/* 1. 复制数组 */
let arr1 = [1, 2, 3];
let arr2 = [...arr1];
let arr3 = arr1.slice()
/* 2. 拼接数组 */
let a1 = [1, 2], a2 = [3, 4];
let a3 = [...a1, ...a2];
let a4 = a1.concat(a2);
构造对象
- 复制对象(浅拷贝);
- 拼接对象。
/* 1. 复制对象 */
let obj1 = { a: 1, b: 2 }, obj2 = { a: 1, c: 3 };
let o = { ...obj1 };
let oo = Object.assign(obj1);
/* 2. 拼接对象 */
let a1 = [1, 2], a2 = [3, 4];
let a3 = [...a1, ...a2];
let a4 = a1.concat(a2);
字符串的展开
- 将字符串展开成数组;
- 能够正确识别任意字节长度的 Unicode 字符。
[...'hi'] // ['h', 'i']
[...'\u4e2d\u56fd!'] // ['中', '国', '!']
/* 4个字节的 Unicode 字符,如一个火箭表情 🚀 */
'\ud83d' // 😀
'\ud83d\ude80' // 🚀
'\ud83d\ude80'.length // 2
[...'\ud83d\ude80'].length //1
生成器的展开
- 要展开可穷尽的生成器;
function* gen() {
yield* [1, 2, 3]
}
let x = [...gen()];
x // [1, 2, 3]
function* fibonacci() {
let current = 0;
let next = 1;
while (true) {
const reset = yield current;
[current, next] = [next, next + current];
if (reset) {
current = 0;
next = 1;
}
}
}
let xx = [...fibonacci()] // 崩掉
展开语法 VS 剩余参数语法
- 都是使用
...
,含义却像正逆运算; - 剩余参数语法:在函数声明时,将剩余参数收纳到最后一个参数中;
- 展开语法:在函数调用时,将可迭代对象的元素展开到参数列表中。
function sum(first=0, ...rest) {
let total = first;
for (const elem of rest) total += +elem;
return total;
}
sum(1, 2, 3, 4) // 10
// 第一个参数 1对应first,其他被收纳到 rest(rest=[2,3,4])
sum(...[1, 2, 3, 4]) // 10
// ...[1, 2, 3, 4] 先展开,等价于 sum(1, 2, 3, 4)
sum(...[], ...[1, 2], 3, 4)