深入了解 ES6 强大的 ... 运算符

2019 年 10 月 16 日 前端大全

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

作者:皮小蛋

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(12345); // [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(123);

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

}

// ES6 时代

function foo(...args// 搜集参数到 args

  args.push(456)

  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 实用开发技巧


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

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

好文章,我在看❤️

登录查看更多
0

相关内容

【2020新书】实战R语言4,323页pdf
专知会员服务
100+阅读 · 2020年7月1日
【2020新书】使用高级C# 提升你的编程技能,412页pdf
专知会员服务
57+阅读 · 2020年6月26日
一份简明有趣的Python学习教程,42页pdf
专知会员服务
76+阅读 · 2020年6月22日
简明扼要!Python教程手册,206页pdf
专知会员服务
47+阅读 · 2020年3月24日
【干货书】流畅Python,766页pdf,中英文版
专知会员服务
224+阅读 · 2020年3月22日
机器学习速查手册,135页pdf
专知会员服务
338+阅读 · 2020年3月15日
【干货】大数据入门指南:Hadoop、Hive、Spark、 Storm等
专知会员服务
95+阅读 · 2019年12月4日
图解NumPy,这是理解数组最形象的一份教程了
机器之心
6+阅读 · 2019年7月12日
PyTorch & PyTorch Geometric图神经网络(GNN)实战
专知
81+阅读 · 2019年6月1日
实战 | 用Python做图像处理(三)
七月在线实验室
15+阅读 · 2018年5月29日
Python3爬虫之入门和正则表达式
全球人工智能
7+阅读 · 2017年10月9日
学员笔记||Python数据分析之:numpy入门(一)
七月在线实验室
7+阅读 · 2017年9月28日
Meta-Learning to Cluster
Arxiv
17+阅读 · 2019年10月30日
Multi-Grained Named Entity Recognition
Arxiv
6+阅读 · 2019年6月20日
Arxiv
7+阅读 · 2019年5月31日
Arxiv
4+阅读 · 2018年10月31日
Arxiv
7+阅读 · 2018年8月28日
The Matrix Calculus You Need For Deep Learning
Arxiv
12+阅读 · 2018年7月2日
Arxiv
5+阅读 · 2018年5月1日
Arxiv
8+阅读 · 2018年3月17日
VIP会员
相关VIP内容
【2020新书】实战R语言4,323页pdf
专知会员服务
100+阅读 · 2020年7月1日
【2020新书】使用高级C# 提升你的编程技能,412页pdf
专知会员服务
57+阅读 · 2020年6月26日
一份简明有趣的Python学习教程,42页pdf
专知会员服务
76+阅读 · 2020年6月22日
简明扼要!Python教程手册,206页pdf
专知会员服务
47+阅读 · 2020年3月24日
【干货书】流畅Python,766页pdf,中英文版
专知会员服务
224+阅读 · 2020年3月22日
机器学习速查手册,135页pdf
专知会员服务
338+阅读 · 2020年3月15日
【干货】大数据入门指南:Hadoop、Hive、Spark、 Storm等
专知会员服务
95+阅读 · 2019年12月4日
相关资讯
图解NumPy,这是理解数组最形象的一份教程了
机器之心
6+阅读 · 2019年7月12日
PyTorch & PyTorch Geometric图神经网络(GNN)实战
专知
81+阅读 · 2019年6月1日
实战 | 用Python做图像处理(三)
七月在线实验室
15+阅读 · 2018年5月29日
Python3爬虫之入门和正则表达式
全球人工智能
7+阅读 · 2017年10月9日
学员笔记||Python数据分析之:numpy入门(一)
七月在线实验室
7+阅读 · 2017年9月28日
相关论文
Meta-Learning to Cluster
Arxiv
17+阅读 · 2019年10月30日
Multi-Grained Named Entity Recognition
Arxiv
6+阅读 · 2019年6月20日
Arxiv
7+阅读 · 2019年5月31日
Arxiv
4+阅读 · 2018年10月31日
Arxiv
7+阅读 · 2018年8月28日
The Matrix Calculus You Need For Deep Learning
Arxiv
12+阅读 · 2018年7月2日
Arxiv
5+阅读 · 2018年5月1日
Arxiv
8+阅读 · 2018年3月17日
Top
微信扫码咨询专知VIP会员