拓展运算符
拓展运算符(spread)是三个点(...)。它好比rest参数的逆运算,将一个数组转为用逗号分隔的参数序列
console.log(...[1, 2, 3]); // 1 2 3
console.log(1, ...[2, 3, 4], 5); // 1 2 3 4 5
console.log([...document.querySelectorAll('div')]); //) [div#app, div#nav.nav, div, div, div, div]
该运算符主要用于函数调用
function pushArray(array, ...items) {
array.push(...items);
}
function add(x, y) {
return x + y
}
let numbers1 = [4, 66]
console.log(add(...numbers1)); //70
pushArray(numbers1, ...[1, 2, 3])
console.log(numbers1);// [4, 66, 1, 2, 3]
上面代码中,array.push(...items)和add(...numbers1)这两行,都是函数的调用,它们都使用了拓展运算符。该运算符将一个数组,变为参数序列
拓展运算符与正常的函数参数结合使用
function f(v, w, x, y, z) {
console.log(v, w, x, y, z)
}
const args = [0, 1];
f(-1, ...args, 2, ...[3]); // -1 0 1 2 3
拓展运算符后面还可以放置表达式
let x = 1;
const arr = [...(x > 0 ? ['a'] : []), 'b'];
console.log(arr);//["a", "b"]
如果拓展运算符后面是一个空数组,则不产生任何效果
console.log([...[], 1]);// [1]
替代函数的apply方法
//ES5写法
function f1(x, y, z) {
console.log(x, y, z)
}
var args1 = [1, 2, 3]
f1.apply(null, args1); // 1 2 3
//ES6写法
function f2(x, y, z) {
console.log(x, y, z)
}
var args2 = [4, 5, 6]
f2(...args2); //4 5 6
一个🌰:拓展运算符取代apply的实际例子,应用Math.max方法,简化求出一个数组最大元素的写法
//ES5写法
console.log(Math.max.apply(null, [10, 20, 30]));// 30
//ES6写法
console.log(Math.max(...[10, 20, 30]));// 30
//等同于
console.log(Math.max(10, 20, 30));// 30
由于JavaScript不提供求数组最大元素的函数,所以只能套用Math.max函数,将数组转为一个参数序列,然后求最大值,有了拓展运算符以后,可以直接使用Math.max了
//ES5写法
var arr1 = [1, 2, 3];
var arr2 = [4, 5, 6];
Array.prototype.push.apply(arr1, arr2);
console.log(arr1); //[1, 2, 3, 4, 5, 6]
//ES6写法
let arr1 = [1, 2, 3];
let arr2 = [4, 5, 6];
arr1.push(...arr2);
console.log(arr1); // [1, 2, 3, 4, 5, 6]
另外一个🌰
//ES5写法
var date1 = new(Date.bind.apply(Date, [null, 2018, 1, 1]));
console.log(date1); // Thu Feb 01 2018 00:00:00 GMT+0800 (中国标准时间)
//ES6写法
let date2 = new Date(...[2018, 1, 1]);
console.log(date2); // Thu Feb 01 2018 00:00:00 GMT+0800 (中国标准时间)
拓展运算符的应用
复制数组
数组时复合的数据类型,直接复制的话,只是复制了指向底层数据结构的指针,而不是克隆了一个全新的数组(也就是说修改其中一个数组,另一个也会改变,因为两个指针同时指向一份数据)
//ES6中的数组克隆
const arr1 = [1, 2, 3];
//写法1
const arr2 = [...arr1];
//写法2
const [...arr3] = arr1;
arr3[0] = 0;
console.log(arr1); //[1, 2, 3]
console.log(arr2); //[1, 2, 3]
console.log(arr3); //[0, 2, 3]
合并数组
let arr1 = [1, 2];
let arr2 = [3];
let arr3 = [4, 5];
let arr5;
let arr6;
//ES5合并
arr5 = arr1.concat(arr2, arr3);
console.log(arr5); // [1, 2, 3, 4, 5]
//ES6合并
arr6 = [...arr1, ...arr2, ...arr3];
console.log(arr6); // [1, 2, 3, 4, 5]
与结构赋值结合
function arr(list, a, ...rest) {
list = [a, ...rest];
return list
}
console.log(arr([], "a", ...["b", "v"])); //["a", "b", "v"]
需要注意的是,如果将拓展运算符用于数组赋值,只能放在参数的最后一位,否则报错
字符串
使用拓展运算符将字符串转为数组
console.log([...'Hello,Mr']); //["H", "e", "l", "l", "o", ",", "M", "r"]
实现Iterator接口的对象
任何Iterator接口的对象,都可以用拓展运算符转为真正的数组
let nodeList = document.querySelectorAll('div');
let array = [...nodeList];
console.log(array); //[div#app, div#nav.nav, div, div, div, div]
上面代码中。querySelectorAll方法返回的是一个NodeList对象。它不是数组,而是类似数组的对象,使用拓展运算符可以将其转为真正的数组,原因在于NodeList对象实现了Iterator
Map和Set结构,Generator函数
拓展运算符内部调用的是数组结构的Iterator接口,因此只要具有Iterator接口的对象,都可以使用拓展运算符
Map结构
let map = new Map([
[1, 'a'],
[2, 'b'],
[3, 'c']
])
let arr = [...map.keys()];
console.log(arr); // [1, 2, 3]
Generator函数运行后,返回一个遍历器对象,因此也可以使用拓展运算符
const go = function*() {
yield 1;
yield 2;
yield 3;
}
//变量go是一个Generator函数,执行后返回的是一个遍历器对象,对这个遍历器对象执行拓展运算符,就会将内部遍历得到的值,转为一个数组
let arr = [...go()];
console.log(arr);//[1, 2, 3]
网友评论