1. rest 参数,也称剩余参数,形式为 ...变量名,用于获取函数多余的参数
  2. 获取函数剩余参数的方式:
    1. 通过 rest 参数获取(推荐)
    2. 通过 arguments 对象获取(不推荐)
  3. rest 参数是一个真数组,多余的变量都会丢到这个数组中
  4. rest 参数之后不能再有其他参数(即只能是最后一个参数),否则会报错
  5. f.length 不计算 rest 参数
  6. 剩余参数和展开运算符使用的都是 ...
    1. 如果在形参位置使用表示剩余参数
    2. 否则,表示展开运算符
  7. 使用 arguments 获取函数剩余参数的弊端
    1. 用它来操作形参,程序的可读性不好(我们无法通过函数的参数列表,一眼得知该函数在内部是否有使用 arguments 来操作形参)
    2. 和形参配合使用,容易出现误操作
      1. 非严格模式下 arguments 和形参之间会有映射关系
      2. 严格模式下 arguments 和形参之间不存在映射关系

剩余参数(rest)

ES6 引入 rest 参数(形式为...变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest 参数搭配的变量是一个数组,该变量将多余的参数放入数组中。

  1. function add(...values) {
  2. let sum = 0;
  3. for (var val of values) {
  4. sum += val;
  5. }
  6. return sum;
  7. }
  8. add(2, 5, 3) // 10

上面代码的add函数是一个求和函数,利用 rest 参数,可以向该函数传入任意数目的参数。

下面是一个 rest 参数代替arguments变量的例子。

  1. // arguments变量的写法
  2. function sortNumbers() {
  3. return Array.from(arguments).sort();
  4. }
  5. // rest参数的写法
  6. const sortNumbers = (...numbers) => numbers.sort();

上面代码的两种写法,比较后可以发现,rest 参数的写法更自然也更简洁。

arguments对象不是数组,而是一个类似数组的对象。所以为了使用数组的方法,必须使用Array.from先将其转为数组。rest 参数就不存在这个问题,它就是一个真正的数组,数组特有的方法都可以使用。下面是一个利用 rest 参数改写数组push方法的例子。

  1. function push(array, ...items) {
  2. items.forEach(function(item) {
  3. array.push(item);
  4. console.log(item);
  5. });
  6. }
  7. var a = [];
  8. push(a, 1, 2, 3)

注意,rest 参数之后不能再有其他参数(即只能是最后一个参数),否则会报错。

  1. // 报错
  2. function f(a, ...b, c) {
  3. // ...
  4. }

函数的length属性,不包括 rest 参数。

  1. (function(a) {}).length // 1
  2. (function(...a) {}).length // 0
  3. (function(a, ...b) {}).length // 1

剩余参数和展开运算符使用的都是 ...,因此需要同注意它们的差异:

  1. 如果在形参位置使用表示剩余参数
  2. 否则,表示展开运算符

分析 arguments 的缺陷

  1. [误操作] 如果和形参配合使用,容易导致混乱。
  2. [可读性极差] 从语义上,使用arguments获取参数,由于形参缺失,无法从函数定义上理解函数的真实意图。

arguments 这东西以后就不要用了,因为有了更好的东西(剩余参数)来替代它,没有必要使用 arguments 而且它还存在一些问题。 因为操作 arguments 在非严格模式下 有可能会改变形参。 但是通过形参名,我们也可以改变形参。 这样就会导致一个问题 有两股力量都可以修改它。 若代码量过多,我们也许就不易分辨到底形参的值是怎么被改变的了。

除了这个问题外,其实还有一个问题,就是我们通过 arguments 来改变形参值会很奇怪。 我们在改变某个变量名的时候,一般都是直接给这个变量名重新赋值,而通过 arguments 来改变形参名的话,看起来就不那么直观。 虽然我们知道它和形参有映射关系,但是终归还是不那么直观,试想一下,若一个函数的参数过多,那么我们还使用 arguments 来间接的操作形参的话,此时我们想要与参数逐一对应起来,就会很繁琐。

demo:不定参求和

做法1:将参数打包成一个数组传递到函数中

  1. function sum(arr) {
  2. let result = 0;
  3. for (let i = 0; i < arr.length; i++) {
  4. result += arr[i];
  5. }
  6. return result;
  7. }
  8. sum([1]); // => 1
  9. sum([1, 2]); // => 3
  10. sum([1, 2, 3]); // => 6
  11. sum([1, 2, 3, 4]); // => 10

做法2:使用 arguments

  1. function sum() {
  2. let result = 0;
  3. for (let i = 0; i < arguments.length; i++) {
  4. result += arguments[i];
  5. }
  6. return result;
  7. }
  8. sum(1); // => 1
  9. sum(1, 2); // => 3
  10. sum(1, 2, 3); // => 6
  11. sum(1, 2, 3, 4); // => 10
  12. /*
  13. 此时 sum 在定义的时候 明明没有定义形参
  14. 但是该函数实际上是在内部通过 arguments 来处理接收到的形参
  15. 这样的做法 无法从函数定义上理解函数的真实意图
  16. */

做法3:使用剩余参数

  1. function sum(...args) {
  2. console.log(args, Array.isArray(args));
  3. let result = 0;
  4. for (let i = 0; i < args.length; i++) {
  5. result += args[i];
  6. }
  7. return result;
  8. }
  9. sum(); // => 0
  10. sum(1); // => 1
  11. sum(1, 2); // => 3
  12. sum(1, 2, 3); // => 6
  13. sum(1, 2, 3, 4); // => 10