arguments 和 剩余函数

196 阅读3分钟

在写apply,call,bind 和 new的手写题的时候经常会碰到 arguments 的使用,虽然知道它的使用,但是更想系统的学习一下,以及arguments和剩余函数的类比。

先来一道面试常考的题目:

箭头函数为什么没有arguments

我也是在搜箭头函数的arguments的时候,google给我跳出来的第一个链接。 其实这个问法是不正确的。经过试验,箭头函数是有arguments的。

function foo() {
  setTimeout(() => {
    console.log("args:", arguments);
  }, 1);
}

foo(1, 2, 3, 4);   //输出 args: Arguments(4) [1, 2, 3, 4, callee: ƒ, Symbol(Symbol.iterator): ƒ]

那么为什么箭头函数有arguments呢。 其实这要从js的词法作用域讲起。箭头函数本身是没有arguments的这个变量的,在箭头函数内部是将arguments这个当做普通参数进行执行的。

function foo() {
  let a = 1
  setTimeout(() => {
    // console.log("args:", arguments);
    console.log('a=',a);   //  箭头函数,在执行之前就通过词法作用域,绑定了a
  }, 1);
}

foo(1, 2, 3, 4);

上面这个例子,可以很清楚的看懂。箭头函数中的arguments是和 a完全类似的。 进一步拓展 箭头函数不仅没有自己的arguments 甚至连 thissupernew.target 这些都是没有的,都要通过作用域来确定,也就是这些都指向父级作用域的 thissupernew.target

arguments 对象

MDN上关于arguments是这样定义的:

arguments 是一个对应于传递给函数的参数的类数组对象。

需要注意 一下 ,arguments 是类数组对象。它除了length属性和索引元素之外没有任何Array属性。

// 这些都能生效
arguments.length
arguments[1] 

但是它是没有数组原型上的方法的,那么我们实在实际开发的时候要用到很多数组的方法,自然而然的要了解将arguments 转化为数组Array的方法 这里列出了一下几个方法

var args = [].slice.call(arguments);
const args = Array.from(arguments);
const args = [...arguments];

剩余参数

先来看MDN关于剩余参数的定义

剩余参数语法允许我们将一个不定数量的参数表示为一个数组。

function sum(...theArgs) {
  return theArgs.reduce((previous, current) => {
    return previous + current;
  });
}

console.log(sum(1, 2, 3));
// expected output: 6

console.log(sum(1, 2, 3, 4));
// expected output: 10

// 如果函数的最后一个命名参数以`...`为前缀,则它将成为一个由剩余参数组成的真数组,
// 其中从`0`(包括)到`theArgs.length`(排除)的元素由传递给函数的实际参数提供。
function(a, b, ...theArgs) {
  // ...
}

在上面的例子中,theArgs将收集该函数的第三个参数(因为第一个参数被映射到a,而第二个参数映射到b)和所有后续参数。

argument对象 和 剩余参数的区别

  1. 剩余参数只包含那些没有对应形参的实参,而 arguments 对象包含了传给函数的所有实参。
  2. arguments对象不是一个真正的数组,而剩余参数是真正的 Array实例,也就是说你能够在它上面直接使用所有的数组方法,比如 sortmapforEachpop
  3. arguments对象还有一些附加的属性 (如callee属性)