持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第21天,点击查看活动详情
扩展运算符,或者展开运算符,江湖人称,三个点(...),用于将可迭代对象展开到其单独的元素中,例如:数组、
字符串、数组、对象、Map 、Set 、Nodelist等。
扩展运算符可以将一个数组转为用逗号分隔的参数序列
console.log(1, ...[2, 3, 4], 5)
// 1 2 3 4 5
1. 将字符串转为字符数组
console.log([...'hello'])
// ['h', 'e', 'l', 'l', 'o']
2. 将NodeList转为数组
NodeList 对象是DOM节点的集合,它类似于数组,但不是数组,可以使用 forEach() 和for..of...来遍历,但没有find、map、filter 等数组方法。
通过扩展运算符可以将其转为数组,以获取掘金首页元素为例:
const nodeList = document.querySelectorAll('.info-box')
console.log(nodeList)
const nodeArr = [...nodeList]
console.log(nodeArr)
3. 函数传参
function add(x, y) {
return x + y;
}
const numbers = [10, 21];
add(...numbers) // 31
注意:只有函数调用时,扩展运算符才可以放在圆括号中,否则会报错
(...[1, 2])
// Uncaught SyntaxError: Unexpected number
console.log((...[1, 2]))
// Uncaught SyntaxError: Unexpected number
console.log(...[1, 2])
// 1 2
4. 替代apply方法
ES5提供的将数组转换为函数参数的方法是使用apply():
// ES5 的写法
function f(x, y, z) {
// ...
}
var args = [0, 1, 2];
f.apply(null, args);
比如JavaScript不提供求数组最大元素的函数,只能将数组转为一个参数序列,然后使用Math.max()求最大值
// ES5 的写法
Math.max.apply(null, [14, 3, 77])
// 等同于
Math.max(14, 3, 77);
有了扩展运算符以后,就可以直接用Math.max()了:
// ES6 的写法
Math.max(...[14, 3, 77])
// 等同于
Math.max(14, 3, 77);
5. 复制数组
复制一层深拷贝:
const arr1 = [1, 2, 3];
// 写法1
const arr2 = [...arr1];
// 写法2
const [...arr2] = [arr1]
arr2.push(4);
console.log(arr1) // [1, 2, 3]
console.log(arr2) // [1, 2, 3, 4]
注意:数组的空位会用undefined来补上
let arr1 = [1, , 3]
arr2 = [...arr3]
console.log(arr2); // [1, undefined, 3]
复制两层及以上浅拷贝:
const arr1 = [[1], 2, 3];
const arr2 = [...arr1];
arr2[0].push(4);
console.log(arr1); // [ [ 1, 4 ], 2, 3 ]
console.log(arr2); // [ [ 1, 4 ], 2, 3 ]
6. 合并数组
const arr1 = [1, 2];
const arr2 = [3, 4];
const arr3 = [...arr1, ...arr2]
console.log(arr3) // [ 1, 2, 3, 4]
7. 分割数组
扩展运算符可以与解构赋值结合来切割原先数组,从而用于生成新的数组
const [first, ...rest] = [1, 2, 3, 4, 5];
console.log(first) // 1
console.log(rest) // [2, 3, 4, 5]
const [first, ...rest] = [];
console.log(first) // undefined
console.log(rest) // []
const [first, ...rest] = ["foo"];
console.log(first) // "foo"
console.log(rest) // []
注意:只能将扩展运算符放在最后
const [...butLast, last] = [1, 2, 3, 4, 5];
// SyntaxError: Rest element must be last element
const [first, ...middle, last] = [1, 2, 3, 4, 5];
// SyntaxError: Rest element must be last element
8. 数组去重
与 Set 一起使用消除数组的重复项:
const arr = [1, 2, 3, 4, 5, 2, 5];
const newArr = [...new Set(arr)];
console.log(newArr); // [ 1, 2, 3, 4, 5 ]
对象的扩展运算符,只会返回参数对象自身的、可枚举的属性
class C {
p = 12;
m() {}
}
let c = new C();
let clone = { ...c };
clone.p; // ok
clone.m(); // 报错
如果扩展运算符后面不是对象,则会自动将其转为对象:
{...1} // {}
{...true} // {}
{...undefined} // {}
{...null} // {}
如果扩展运算符后面是一个空对象,则没有任何效果:
{...{}, a: 1} // { a: 1 }
如果扩展运算符后面是字符串,它会自动转成一个类似数组的对象:
{...'hello'} // {0: "h", 1: "e", 2: "l", 3: "l", 4: "o"}
由于数组是特殊的对象,所以对象的扩展运算符也可以用于数组:
{ ...['a', 'b', 'c'] }; // {0: "a", 1: "b", 2: "c"}
9. 复制对象
等同于使用Object.assign()方法
复制一层深拷贝
let obj1 = { a: 1, b: 2 };
let obj2 = { ...obj1 }; // 等同于 let obj2 = Object.assign({}, obj1);
obj2.a = 3;
console.log(obj1); // { a: 1, b: 2 }
console.log(obj2); // { a: 3, b: 2 }
复制两层及以上浅拷贝
let obj1 = { a: { b: 1 }, c: 2 };
let obj2 = { ...obj1 }; // 等同于 let obj2 = Object.assign({}, obj1);
obj2.a.b = 3;
console.log(obj1); // { a: { b: 3 }, c: 2 }
console.log(obj2); // { a: { b: 3 }, c: 2 }
10. 合并对象
let obj1 = { a: 1 };
let obj2 = { b: 2 };
let obj3 = {...obj1, ...obj2} // 等同于let obj3 = Object.assign({}, obj1, obj2);
console.log(obj3); // { a: 1, b: 2 }
如果用户自定义的属性,放在扩展运算符后面,则扩展运算符内部的同名属性会被覆盖掉:
let obj1 = { a: 1 };
let obj2 = { b: 2 };
let obj3 = { ...obj1, ...obj2, a: 3, c: 4 };
console.log(obj3); // { a: 3, b: 2, c: 4 }
如果把自定义属性放在扩展运算符前面,就变成了设置新对象的默认属性值,同名属性会被扩展运算符中的覆盖掉:
let obj1 = { a: 1 };
let obj2 = { b: 2 };
let obj3 = { a: 3, c: 4, ...obj1, ...obj2 };
console.log(obj3); // { a: 1, c: 4, b: 2 }