熟悉Arguments对象与剩余参数对象

119 阅读4分钟

Arguments

arguments是一个可在函数内部可访问,包含传递给函数参数值的对象;

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

知识点:

1、arguments对象仅在函数内部可使用,且函数不能为arrow function

2、虽然arguments是类数组,但是我们可以使用索引去访问元素,并且它有一个length属性,值为参数的个数;

function test (a, b, c) {
  console.log(arguments.length)
}
test(1)  //1
test(1, 2, 3)  //3

3、arguments对象包含函数调用期间传递的所有参数,即使函数声明中没有那么多;

function test(a, b) {
  console.log(a, b, arguments)
}
test(1, 2, 3, 4, 5, 6) 
// 1, 2, Arrguments[1, 2, 3, 4, 5 ,6, callee: f, length: 6, Symbol(Symbol: iterator): f]

这里可以顺便看到arguments对象的属性:

callee:指向参数所属的当前执行的函数

length: 传递给函数的参数数量

iterator:迭代器对象

arguments类数组转数组

1、方法1: Array.prototype.slice.call(arguments) 或 [].slice.call(arguments)

这里插播一下为什么这里能够转化为数组:

首先从网上扒一下大佬们对slice源码的简化版:


function slice(start, end) { 
  var startToUse = start || 0, 
      endToUse = end || ToUint32(this.length), 
      result = []; 
  for(var i = startToUse; i < endToUse; i++) { 
    result.push(this[i]); 
  }
  return result; 
} 

可见其关键还是slice的实现机制有关,对于满足一定条件的对象(有length属性,key值为数字)即可通过slice转化为返回一个数组; 如

const arrayLike = {
  '0: 'a',
  '1: 'b',
  '2': 'c',
  length: 3
}
const arr = [].slice.call(arrayLike) //['a', 'b', 'c']

插播结束....

2、方法2: Array.form(arguments) 3、方法3: [...arguments]

在arguments上使用typeof

typeof arguments  // 'object'

注意点:

1、要安全使用函数内部的arguments参数,不能将其泄露或者传递出去,such as:

// Leaking arguments example1:
function getArgs() {
    return arguments;
}

// Leaking arguments example2:
function getArgs() {
    const args = [].slice.call(arguments);
    return args;
}

// Leaking arguments example3:
function getArgs() {
    const args = arguments;
    return function() {
        return args;
    };
}

解决方法:创建成一个新数组再返回;

function getArgs() {
    const args = new Array(arguments.length);
    for(let i = 0; i < args.length; ++i) {
        args[i] = arguments[i];
    }
    return args;

引用: 我们每次使用 arguments 时通常第一步都会将其转换为数组,同时 arguments 使用不当还容易导致性能损失,那么为什么不将 arguments 直接设计成数组对象呢?

这需要从这门语言的一开始说起。arguments 在语言的早期就引入了,当时的 Array 对象具有 4 个方法: toString、 join、 reverse 和 sort。arguments 继承于 Object 的很大原因是不需要这四个方法。而现在,Array 添加了很多强大的方法,比如 forEach、map、filter 等等。那为什么现在不在新的版本里让 arguments 重新继承自 Array呢?其实 ES5 的草案中就包含这一点,但为了向前兼容,最终还是被委员会否决了。

2、在sloppy mode下可以对参数进行修改会影响到arguments object,但是在use strict下两者没有联系

3、需要将参数从一个函数传给另一个函数时,推荐通过apply方法:

function foo() {
    bar.apply(this, arguments);
}
function bar(a, b, c) {
}

3、可利用arguments进行模拟函数重载(所谓重载:即函数根据参数的不同而产生不同的调用)

关于arguments的知识点与使用还有很多就不一一说明了,目前留下个疑问:

为什么将arguments传递出去会造成泄露,导致V8引擎没有优化,网上看有人回答说是因为会一直保持对arguments对象的使用,导致V8必须实例化其为JavaScript对象而不是优化为堆栈变量,那以下两种方式为什么会保留了对arguments对象的引用呢?

// Leaking arguments example1:
function getArgs() {
    return arguments;
}

// Leaking arguments example2:
function getArgs() {
    const args = [].slice.call(arguments);
    return args;
}

剩余参数(Rest Parameters)

说明:剩余参数主要是ECMAScript用来替换arguments,可以拿到函数除开始参数外的多余参数;该对象是自定义的普通标识符,只是需要在前面加上三个点:...;

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

提取重点:

1、以...开头,位置上必须在最后一个参数

2、是一个数组;

const testFun = function (...theArgs) {
   console.log(theArgs) // [1, 2, 3, 4, 5, 6]
}

还可以对theArgs进行解构:

function (...[a, b, c]) {
  return a + b + c
}
f(1, 2, 3, 4) // 6

尾语

由于剩余参数是用来替换arguments的,所以用法基本无差,主要是属性有差别,所以今日的熟悉就到这里了,整体并不难,难点为arguments的optimization!!!等我变厉害了再回来补充~~~~