原生js:arguments

58 阅读2分钟

一、arguments是什么

函数调用时,执行第一行代码前,创建当前函数的AO,而AO中默认有两个属性,一个是this,一个是arguments。arguments是一个类数组,存放函数的实参

arguments的本质是对象,不是数组:

      function test() {
        console.log(arguments instanceof Array) // false
        console.log(arguments instanceof Object) // true
        console.log(Object.prototype.toString.call(arguments)) // [object Arguments]
      }

      test()

二、arguments的length(实参的个数)

1、length在调用时确定,往arguments中添加值,不会改变length

      function test() {
        console.log(arguments.length) // 2
        arguments[2] = 3
        console.log(arguments, arguments.length) // [1, 2, 2: 3, callee: f, ...] 2
      }

      test(1, 2)

2、不可以通过length截断arguments

      function test() {
        arguments.length = 1
        console.log(arguments, arguments.length) // [1, 1: 2, 2: 3, 3: 4, callee: f, ...] 1
      }

      test(1, 2, 3, 4)

可以手动改变arguments.length,但是不会对arguments造成实质性的截断

三、arguments和形参、实参之间的映射关系

  1. 如果形参有对象的实参,那么形参和arguments之间有映射关系,可以相互改变
  2. 指定的形参如果没有对应的实参,那么形参和arguments之间没有映射关系
  3. 有映射关系不代表访问同一块内存,只是保持同步变化
      function test(a, b, c) {
        // a = 10
        // b = 20
        // c = 30
        arguments[0] = 10
        arguments[1] = 20
        arguments[2] = 30
        console.log(a, b, c) // 10 20 undefined
        console.log(arguments[0], arguments[1], arguments[2]) // 10 20 30
      }

      test(1, 2)

形参a、b由于有对应的实参,和arguments之间有映射关系;形参c没有对应的实参,和arguments之间没有映射关系

四、ES6中的arguments

1、如果形参有初始值,那么形参和arguments之间的映射关系就没有了

      function test(a = 3, b) {
        a = 10
        console.log(a, b) // 10 2
        console.log(arguments[0], arguments[1]) // 1 2
      }

      test(1, 2)

总结:形参和arguments之间映射关系需要满足2个条件

  1. 形参必须要有对应的实参
  2. 形参没有初始值

2、箭头函数中没有arguments

      const test = () => {
        console.log(arguments) // Uncaught ReferenceError: arguments is not defined
      }

      test()

如果需要获取实参,使用剩余运算符(...)获取

      const test = (...rest) => {
        console.log(rest) // [1, 2, 3]
      }

      test(1, 2, 3)

五、callee和caller

  • callee是当前函数内置对象arguments的属性,指向当前函数
  • caller是当前函数的属性。谁调用了当前函数,就返回谁,如果是在a函数中调用了b函数,那么b.caller指向a函数;如果在全局调用了b函数,b.caller指向null。严格模式下使用caller会报错
      function parent() {
        son(1, 2, 3)
      }
      function son(a, b) {
        console.log(son.caller) // f parent(){}
        console.log(son.length) // 形参的个数:2
        console.log(arguments.length) // 实参的个数:3
        console.log(arguments.callee === son) // true
      }
      parent()

callee的使用场景:匿名函数调用自己

      function recursion() {
        return function (n) {
          if (n < 1) {
            return 1
          } else {
            return n * arguments.callee(n - 1)
          }
        }
      }

      const result = recursion()(5)
      console.log(result)