深入了解 ES6 强大的 ... 运算符
发布于 1 个月前 作者 yan 56 次浏览

(给前端大全加星标,提升前端技能)

作者:皮小蛋

https://segmentfault.com/a/1190000020612737

背景 ...  运算符,是  ES6  里一个新引入的运算法,也叫 展开/收集 运算符,我们每天都要和它打交道。 这篇文章,我就带你系统的回顾下这个运算符,介绍一些基础和进阶的用法。 基础篇

先看一下官方描述:

Spread syntax allows an iterable, such as an array expression or string, to be expanded in places where 0 or more arguments or elements are expected or an object expression to be expanded in places where 0 or more key-value pairs (for object literals) are expected.

简而言之就是,… 运算符可以展开一个可迭代对象重的所有项。

可迭代的对象一般是指可以被循环的,包括:string, array, set 等等。下面我们来看几个基础的例子来加深理解。

基础用法

**基础用法 1: ** 展开

const a = [2, 3, 4]  const b = [1, …a, 5]

b; // [1, 2, 3, 4, 5]

**基础用法 2: ** 收集

function foo(a, b, …c) {     console.log(a, b, c)      }

foo(1, 2, 3, 4, 5); // 1, 2, [3, 4, 5]

如果没有命名参数的话,… 就会收集所有的参数:function foo(…args) {     console.log(args)      }

foo(1, 2, 3, 4, 5); // [1, 2, 3, 4, 5]

关于这个收集的用法,官方描述: “A function’s last parameter can be prefixed with … which will cause all remaining (user supplied) arguments to be placed within a “standard” javascript array. Only the last parameter can be a rest parameter.”

这个运算符一定是在最后一个参数的位置,也很好理解,就是“收集前面剩下的参数”。

Remember that the rest parameter must be the last parameter, or an error will occur.

如果不在最后一位,会报错。

不得不感叹,这个运算符设计的真的是妙,可展开,可收集,收放自如,当真好用。

**基础用法 3: 把 ** 类数组 ** 转换为 ** 数组

先回顾下什么是类数组吧. 类数组和数组非常接近,都可以拥有一系列元素,也有length 属性,最大的不同是: 类数组不具备数组的一系列方法。 举个例子:

const nodeList = document.getElementsByClassName(“test”); const array = […nodeList];

console.log(nodeList); //Result: HTMLCollection [ div.test, div.test ] console.log(array); //Result: Array [ div.test, div.test ]

使用 … 就可以实现类数组到数组的转换,转换之后,就可以使用数组的各种方法了。 你还记得在这个操作符出来之前是如何转换的吗? 这个问题还是头条的一个前端面试题。 看例子:// ES5 时代 function bar() {   var args = Array.prototype.slice.call(arguments);

// 调用push 加几个元素   args.push(1, 2, 3);

// 把args 作为参数传递给foo   foo.apply(null, args)

}

// ES6 时代

function foo(…args) { // 搜集参数到 args

args.push(4, 5, 6)

console.log(…args) // 展开args

}

bar(0); // 0 1 2 3 4 5 6

**基础用法 4: ** 增加元素或属性

1: 为数组新增成员

const pokemon = [‘KK’, ‘Peter’]; const charmander = ‘郑伊健’;

const pokedex = […pokemon, charmander];

console.log(pokedex);

//Result: [ ‘KK’, ‘Peter’, ‘郑伊健’ ]

2: 为对象新增属性

const basicSquirtle = { name: ‘Squirtle’, type: ‘Water’ }; const fullSquirtle = {   …basicSquirtle,   species: ‘Tiny Turtle’,   evolution: ‘Wartortle’ };

console.log(fullSquirtle);

//Result: { name: ‘Squirtle’, type: ‘Water’, species: ‘Tiny Turtle’, evolution: ‘Wartortle’ }

**基础用法 5: ** 合并数组/对象

合并数组:

const pokemon = [‘Squirtle’, ‘Bulbasur’, ‘Charmander’]; const morePokemon = [‘Totodile’, ‘Chikorita’, ‘Cyndaquil’];

const pokedex = […pokemon, …morePokemon];

console.log(pokedex);  //Result: [ ‘Squirtle’, ‘Bulbasur’, ‘Charmander’, ‘Totodile’, ‘Chikorita’, ‘Cyndaquil’ ]

// 对象数组也一样: const pokemon = [   { name: ‘Squirtle’, type: ‘Water’ },   { name: ‘Bulbasur’, type: ‘Plant’ } ]; const morePokemon = [{ name: ‘Charmander’, type: ‘Fire’ }];

const pokedex = […pokemon, …morePokemon];

console.log(pokedex);

//Result: [ { name: ‘Squirtle’, type: ‘Water’ }, { name: ‘Bulbasur’, type: ‘Plant’ }, { name: ‘Charmander’, type: ‘Fire’ } ]

合并对象 const baseSquirtle = {   name: ‘Squirtle’,   type: ‘Water’ };

const squirtleDetails = {   species: ‘Tiny Turtle Pokemon’,   evolution: ‘Wartortle’ };

const squirtle = { …baseSquirtle, …squirtleDetails }; console.log(squirtle);  //Result: { name: ‘Squirtle’, type: ‘Water’, species: ‘Tiny Turtle Pokemon’, evolution: ‘Wartortle’ }

以上是一些基础费用法 下面介绍一些… 操作符的进阶用法。 进阶篇

1. 复制具有嵌套结构的数据/对象

先看一个例子:const pokemon = {   name: ‘Squirtle’,   type: ‘Water’,   abilities: [‘Torrent’, ‘Rain Dish’] };

const squirtleClone = { …pokemon };

pokemon.name = ‘Charmander’; pokemon.abilities.push(‘Surf’);

console.log(squirtleClone);

//Result: { name: ‘Squirtle’, type: ‘Water’, abilities: [ ‘Torrent’, ‘Rain Dish’, ‘Surf’ ] }

当我们修改原对象的 name 属性时,我们的克隆对象的 name 属性没有受影响,这是符合我们预期的。 但是当修改原对象的 abilities 属性时,我们的克隆对象也被修改了。 原因也很简单,因为复制过来的 abilities 是一个引用类型,原数据改了,用到他的地方也会跟着改。 知道原因,再解决就很简单了,两种方式:

1、复制引用类型的数据

const pokemon = {   name: ‘Squirtle’,   type: ‘Water’,   abilities: [‘Torrent’, ‘Rain Dish’] };

const squirtleClone = { …pokemon, abilities: […pokemon.abilities] };

pokemon.name = ‘Charmander’; pokemon.abilities.push(‘Surf’);

console.log(squirtleClone);

//Result: { name: ‘Squirtle’, type: ‘Water’, abilities: [ ‘Torrent’, ‘Rain Dish’ ] }

这样就 OK 了###

2、深克隆

在这里就不多解释了。

2. 增加条件属性

顾名思义,就是需要根据条件添加的属性。看个例子:const pokemon = {   name: ‘Squirtle’,   type: ‘Water’ };

const abilities = [‘Torrent’, ‘Rain dish’]; const fullPokemon = abilities ? { …pokemon, abilities } : pokemon;

console.log(fullPokemon);

3 **. ** 短路

const pokemon = {   name: ‘Squirtle’,   type: ‘Water’ };

const abilities = [‘Torrent’, ‘Rain dish’]; const fullPokemon = {   …pokemon,   …(abilities && { abilities }) };

console.log(fullPokemon);

如果 abilities 为 true,就相当于是const fullPokemon = {   …pokemon,   …{ abilities } }

这也是一个很有用的技巧。

**4. ** 默认结构和添加默认属性

默认结构:

我们知道,当结构一个对象的时候,如果这个对象里没有某个属性,解出来是undefined , 我们可以添加默认值来解决: const pokemon = {   id: 1,   name: ‘Squirtle’ };

const { type, name } = pokemon; console.log(name); //Result: Squirtle console.log(type); //Result: undefined

//Assigning default value to the type variable const { type = ‘Water’, name } = pokemon; console.log(type); //Result: Water

**

**

添加默认属性

有时候从我们会遇到这样的情况,一个对象,大部分属性是相似的,只有小部分是不不同的,这时候我们就可以设置一个基础对象,具备基础属性,其他的对象可以通过扩展这个对象来得到。

看例子:const pokemon = {   name: ‘Squirtle’,   type: ‘Water’ };

//  给abilities默认赋值 const { abilities = [], …rest } = pokemon;

const fullSquirtle = { …rest, abilities };

console.log(rest); //Result: { name: ‘Squirtle’, type: ‘Water’ } console.log({ fullSquirtle }); //Result: { name: ‘Squirtle’, type: ‘Water’, abilities: [] }

这里就是通过展开 rest , 合并 abilities 得到完全体的数据。

如果有批量的数据需要处理,这种方法也非常方便:const pokemon = [   {     name: ‘Charmander’,     type: ‘Fire’   },   { name: ‘Squirtle’, type: ‘Water’, abilities: [‘Torrent’, ‘Rain Dish’] },   {     name: ‘Bulbasur’,     type: ‘Plant’   } ];

function setDefaultAbilities(object) {   const { abilities = [], …rest } = object;   return { …rest, abilities }; }

// Applying the setDefaultAbilities function to all the pokemon in the array: const normalizedPokemon = pokemon.map(pokemon => setDefaultAbilities(pokemon));

console.log(normalizedPokemon);

//Result: [ { name: ‘Charmander’, type: ‘Fire’, abilities: [] },   { name: ‘Squirtle’, type: ‘Water’, abilities: [ ‘Torrent’, ‘Rain Dish’ ] }, { name: ‘Bulbasur’, type: ‘Plant’, abilities: [] } ]

这样迭代一遍,所有的对象就都具备 abilities 属性了。

总结 … 运算符非常灵活,收放自如,非常强大,希望我们都能很好的掌握这个工具。

内容就这么多,希望对大家有所帮助,如有纰漏,欢迎指正。

推荐阅读

(点击标题可跳转阅读)

ES6 核心特性

ES6 系列之我们来聊聊 Promise

ES6 Javascript 实用开发技巧

觉得本文对你有帮助?请分享给更多人

关注「前端大全」加星标,提升前端技能

好文章,我在看❤️

回到顶部