2021.04.26数组的扩展(9.1扩展运算符)
扩展运算符
扩展运算符(spread)是三个点( ... )。它好比 rest 参数的逆运算,将一个数组转为用逗号
分隔的参数序列。
1. console.log(...[1, 2, 3]) 2. // 1 2 3 3. 4. console.log(1, ...[2, 3, 4], 5) 5. // 1 2 3 4 5 6. 7. [...document.querySelectorAll('div')] 8. // [<div>, <div>, <div>]
该运算符主要用于函数调用。
1. function push(array, ...items) { 2. array.push(...items); 3. } 4. 5. function add(x, y) { 6. return x + y; 7. } 8. 9. const numbers = [4, 38]; 10. add(...numbers) // 42
上面代码中, array.push(...items) 和 add(...numbers) 这两行,都是函数的调用,它们都使
用了扩展运算符。该运算符将一个数组,变为参数序列。
扩展运算符后面还可以放置表达式。
1. const arr = [ 2. ...(x > 0 ? ['a'] : []), 3. 'b', 4. ];
如果扩展运算符后面是一个空数组,则不产生任何效果。
1. [...[], 1] 2. // [1]
替代函数的 apply 方法
由于扩展运算符可以展开数组,所以不再需要 apply 方法,将数组转为函数的参数了。
1. // ES5 的写法 2. function f(x, y, z) { 3. // ... 4. } 5. var args = [0, 1, 2]; 6. f.apply(null, args); 7. 8. // ES6的写法 9. function f(x, y, z) { 10. // ... 11. } 12. let args = [0, 1, 2]; 13. f(...args);
下面是扩展运算符取代 apply 方法的一个实际的例子,应用 Math.max 方法,简化求出一个数组最
大元素的写法。
1. // ES5 的写法 2. Math.max.apply(null, [14, 3, 77]) 3. 4. // ES6 的写法 5. Math.max(...[14, 3, 77]) 6. 7. // 等同于 8. Math.max(14, 3, 77);
复制数组
数组是复合的数据类型,直接复制的话,只是复制了指向底层数据结构的指针,而不是克隆一个全新的数组。
1. const a1 = [1, 2]; 2. const a2 = a1; 3. 4. a2[0] = 2; 5. a1 // [2, 2]
上面代码中, a2 并不是 a1 的克隆,而是指向同一份数据的另一个指针。修改 a2 ,会直接导
致 a1 的变化。
ES5 只能用变通方法来复制数组。
1. const a1 = [1, 2]; 2. const a2 = a1.concat(); 3. 4. a2[0] = 2; 5. a1 // [1, 2]
上面代码中, a1 会返回原数组的克隆,再修改 a2 就不会对 a1 产生影响。
扩展运算符提供了复制数组的简便写法。
1. const a1 = [1, 2]; 2. // 写法一 3. const a2 = [...a1]; 4. // 写法二 5. const [...a2] = a1;
上面的两种写法, a2 都是 a1 的克隆。
合并数组
扩展运算符提供了数组合并的新写法。
1. const arr1 = ['a', 'b']; 2. const arr2 = ['c']; 3. const arr3 = ['d', 'e']; 4. 5. // ES5 的合并数组 6. arr1.concat(arr2, arr3); 7. // [ 'a', 'b', 'c', 'd', 'e' ] 8. 9. // ES6 的合并数组 10. [...arr1, ...arr2, ...arr3] 11. // [ 'a', 'b', 'c', 'd', 'e' ]
不过,这两种方法都是浅拷贝,使用的时候需要注意。
1. const a1 = [{ foo: 1 }]; 2. const a2 = [{ bar: 2 }]; 3. 4. const a3 = a1.concat(a2); 5. const a4 = [...a1, ...a2]; 6. 7. a3[0] === a1[0] // true 8. a4[0] === a1[0] // true
上面代码中, a3 和 a4 是用两种不同方法合并而成的新数组,但是它们的成员都是对原数组成员
的引用,这就是浅拷贝。如果修改了引用指向的值,会同步反映到新数组。
如果将扩展运算符用于数组赋值,只能放在参数的最后一位,否则会报错。
1. const [...butLast, last] = [1, 2, 3, 4, 5]; 2. // 报错 3. 4. const [first, ...middle, last] = [1, 2, 3, 4, 5]; 5. // 报错
字符串
扩展运算符还可以将字符串转为真正的数组。
1. [...'hello'] 2. // [ "h", "e", "l", "l", "o" ]
上面的写法,有一个重要的好处,那就是能够正确识别四个字节的 Unicode 字符。
1. 'x\uD83D\uDE80y'.length // 4 2. [...'x\uD83D\uDE80y'].length // 3
Map 和 Set 结构,Generator 函数
1. let map = new Map([ 2. [1, 'one'], 3. [2, 'two'], 4. [3, 'three'], 5. ]); 6. 7. let arr = [...map.keys()]; // [1, 2, 3]
Generator 函数运行后,返回一个遍历器对象,因此也可以使用扩展运算符。
1. const go = function*(){ 2. yield 1; 3. yield 2; 4. yield 3; 5. }; 6. 7. [...go()] // [1, 2, 3]
上面代码中,变量 go 是一个 Generator 函数,执行后返回的是一个遍历器对象,对这个遍历器对
象执行扩展运算符,就会将内部遍历得到的值,转为一个数组。
如果对没有 Iterator 接口的对象,使用扩展运算符,将会报错。
1. const obj = {a: 1, b: 2}; 2. let arr = [...obj]; // TypeError: Cannot spread non-iterable object
2021-04-26 15:01:20