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

 

posted @ 2021-04-27 10:09  铁打的代码流水的bug  阅读(94)  评论(0)    收藏  举报