ES6 新增了很多特性,扩展运算符就是其中一个。简单的三个点号,让原本繁琐的代码瞬间变得简洁、优雅。就让我们看看它有哪些常见用法吧。

让函数的 Apply 调用方式成为过去

有时候我们需要通过 Function.prototype.apply 来调用函数,参数就放在一个数组里。

function doStuff (x, y, z) { }
var args = [0, 1, 2];

// 调用函数并传参
doStuff.apply(null, args);

现在有了展开运算符,我们可以完全避免使用 apply,只需在调用函数时在数组前加上展开运算符:

doStuff(...args);

代码变得更短、更简洁,也不需要一个毫无用处的 null

合并数组

合并数组有各种方式,但是展开运算符为我们提供了一种新思路:

arr1.push(...arr2) // 在数组后面添加 arr2 里的元素
arr1.unshift(...arr2) //在数组前面添加 arr2 里的元素

如果你想合并两个数组,并且想把元素放到数组中的任何位置,你可以这么做:

var arr1 = ['two', 'three'];
var arr2 = ['one', ...arr1, 'four', 'five'];

// ["one", "two", "three", "four", "five"]

代码少了很多,还可以随意控制位置!

拷贝数组

拷贝数组是个常见的任务,过去我们使用 Array.prototype.slice
来完成,但现在我们可以通过展开运算符得到数组的副本:

var arr = [1,2,3];
var arr2 = [...arr]; // 等同于 arr.slice()
arr2.push(4)

这里要注意,数组里的对象仍然是引用类型的,因此实际上并不是所有内容都被“拷贝”了。

将参数或 NodeList 转成数组

跟拷贝数组类似,我们用 Array.Prototype.sliceNodeListarguments 对象转成真正的数组,但是现在我们可以使用展开运算符完成这个任务:

[...document.querySelectorAll('div')]

你甚至可以从函数签名里得到数组形式的参数:

var myFn = function(...args) {
// ...
}

Array.from也可以做同样的事情。

使用 Math对象里的相关函数

在参数个数不确定的函数里使用展开运算符,再合适不过了。

let numbers = [9, 4, 7, 1];
Math.min(...numbers); // 1

对象解构

解构操作在变量批量赋值上非常方便,省去了繁琐的声明和赋值操作。配合使用展开运算符,进一步简化操作:

let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 };
console.log(x); // 1
console.log(y); // 2
console.log(z); // { a: 3, b: 4 }

常见考点

1. 展开运算符的基础概念

  • Spread Operator(展开运算符):用于展开数组或对象的元素。
  • Rest Operator(剩余运算符):用于将多个参数收集成一个数组或对象。

考察点

  • 理解展开运算符的基本用法和区别。
  • 理解它在数组和对象中的不同用法。

示例问题

  • 请解释展开运算符的作用,并给出一个数组展开的例子。
  • 请解释 ... 是如何在函数参数中作为剩余运算符工作的?

2. 数组的展开(Spread)

展开运算符可以用来展开数组的元素,常见的应用场景有合并数组、复制数组等。

考察点

  • 合并数组:如何使用展开运算符合并两个数组。
  • 复制数组:如何使用展开运算符创建一个数组的浅拷贝。
  • 传递数组作为参数:如何将数组展开为函数的独立参数。

示例问题

  • 如何使用展开运算符将两个数组合并成一个新数组?请给出代码示例。
  • 如何用展开运算符创建一个数组的浅拷贝?

3. 对象的展开(Spread)

在对象中,展开运算符可以用于复制对象,合并多个对象,或者在对象中更新特定字段。

考察点

  • 合并对象:如何将多个对象合并为一个新对象。
  • 复制对象:如何使用展开运算符创建对象的浅拷贝。
  • 对象更新:如何在已有对象基础上更新某个字段(创建新对象并改变特定属性)。
  • 注意浅拷贝:展开运算符只会进行浅拷贝,对象的嵌套属性还是会保持引用。

示例问题

  • 请用展开运算符实现两个对象的合并。
  • 用展开运算符创建一个对象的浅拷贝,并修改某个属性。

4. 剩余运算符(Rest)

剩余运算符用于收集参数,通常用于函数参数中,收集不定数量的参数成一个数组。

考察点

  • 函数参数收集:如何使用 ... 作为函数参数收集多个参数。
  • 解构赋值:如何使用 ... 收集剩余属性(包括数组和对象的解构)。
  • 命名参数:理解剩余运算符在函数参数中的位置(通常是最后一个参数)。

示例问题

  • 如何使用剩余运算符处理函数中不定数量的参数?
  • 在解构赋值中,如何使用剩余运算符来收集剩余的属性或数组元素?

5. 数组和对象的解构赋值

展开运算符在解构赋值中也有重要应用,常常与剩余运算符结合使用,用来提取和分配剩余的元素或属性。

考察点

  • 数组解构:如何使用展开运算符获取数组中的剩余元素。
  • 对象解构:如何在对象解构中使用剩余运算符来获取剩余的属性。

示例问题

  • 如何使用展开运算符来解构一个数组并获取剩余元素?
  • 给定一个对象,如何通过解构和展开运算符获取剩余的属性?

6. 展开运算符的浅拷贝

展开运算符在处理数组和对象时会创建浅拷贝,因此嵌套对象或数组的引用会被共享。

考察点

  • 理解浅拷贝和深拷贝的区别,知道展开运算符只进行浅拷贝。
  • 知道如何利用展开运算符进行对象和数组的浅拷贝,但也理解它的限制。

示例问题

  • 展开运算符在复制对象时会发生什么?它是如何进行浅拷贝的?
  • 使用展开运算符复制一个嵌套对象会出现什么问题?

7. 展开运算符的性能

在大数据量或大量嵌套的情况下,展开运算符可能会影响性能,特别是在深拷贝和大量合并操作时。

考察点

  • 理解展开运算符对性能的影响,特别是在合并大量数据或进行深拷贝时。
  • 对比展开运算符和其他方法(如 Object.assign)的性能。

示例问题

  • 展开运算符在性能上有哪些潜在的陷阱?在处理大量数据时,有没有其他方法更合适?

8. 展开运算符与 Object.assign 比较

Object.assign 和展开运算符都可以用于对象的合并和复制,但它们之间有一些细微的差异。

考察点

  • 理解 Object.assign 和展开运算符的异同,尤其是在合并对象时的行为差异。
  • 理解 Object.assign 是浅拷贝,而展开运算符在对象时行为类似。

示例问题

  • 展开运算符和 Object.assign 的主要区别是什么?你更倾向于使用哪一个?

9. 展开运算符与数组函数的结合

展开运算符常常与数组方法(如 concat, map, filter 等)一起使用。

考察点

  • 使用与数组方法:了解如何结合 concat, map, filter 等方法对数组进行操作。
  • 性能优化:结合数组方法时,如何避免不必要的性能开销。

示例问题

  • 如何使用展开运算符和 concat 方法合并两个数组?
  • 在处理大型数组时,展开运算符如何影响性能?

10. 展开运算符在函数参数中的应用

展开运算符可以用于将一个数组展开为函数的多个参数,或将多个参数收集到一个数组中。

考察点

  • 函数参数展开:理解如何通过展开运算符将数组传递给函数,或者将多个参数聚合为一个数组。
  • 剩余参数:理解如何使用剩余运算符来处理不定数量的参数。

示例问题

  • 请展示如何使用展开运算符将数组传递给函数。
  • 如何使用剩余运算符收集函数参数?