##arguments 对象

JavaScript 中每个函数内都能访问一个特别变量 arguments。这个变量维护着所有传递到这个函数中的参数列表。

注意: 由于 arguments 已经被定义为函数内的一个变量。 因此通过 var 关键字定义 arguments 或者将 arguments 声明为一个形式参数, 都将导致原生的 arguments 不会被创建。

arguments 变量不是一个数组(Array)。 尽管在语法上它有数组相关的属性 length,但它不从 Array.prototype 继承,实际上它是一个对象(Object)。

因此,无法对 arguments 变量使用标准的数组方法,比如 pushpop 或者 slice。 虽然使用 for 循环遍历也是可以的,但是为了更好的使用数组方法,最好把它转化为一个真正的数组。

转化为数组

前端进阶题库 - 每天一道面试题 https://www.javascriptc.com/interview-tips/

下面的代码将会创建一个新的数组,包含所有 arguments 对象中的元素。

    Array.prototype.slice.call(arguments);

这个转化比较,在性能不好的代码中不推荐这种做法。

传递参数

下面是将参数从一个函数传递到另一个函数的推荐做法。

    function foo() {
        bar.apply(null, arguments);
    }
    function bar(a, b, c) {
        // 干活
    }

另一个技巧是同时使用 callapply,创建一个快速的解绑定包装器。

    function Foo() {}

    Foo.prototype.method = function(a, b, c) {
        console.log(this, a, b, c);
    };

    // 创建一个解绑定的 "method"
    // 输入参数为: this, arg1, arg2...argN
    Foo.method = function() {

        // 结果: Foo.prototype.method.call(this, arg1, arg2... argN)
        Function.call.apply(Foo.prototype.method, arguments);
    };

译者注:上面的 Foo.method 函数和下面代码的效果是一样的:

    Foo.method = function() {
        var args = Array.prototype.slice.call(arguments);
        Foo.prototype.method.apply(args[0], args.slice(1));
    };

自动更新

arguments 对象为其内部属性以及函数形式参数创建 gettersetter 方法。

因此,改变形参的值会影响到 arguments 对象的值,反之亦然。

    function foo(a, b, c) {
        arguments[0] = 2;
        a; // 2

        b = 4;
        arguments[1]; // 4

        var d = c;
        d = 9;
        c; // 3
    }
    foo(1, 2, 3);

性能真相

不管它是否有被使用,arguments 对象总会被创建,除了两个特殊情况 - 作为局部变量声明和作为形式参数。

argumentsgetterssetters 方法总会被创建;因此使用 arguments 对性能不会有什么影响。 除非是需要对 arguments 对象的属性进行多次访问。

ES5 提示: 这些 getterssetters 在严格模式下(strict mode)不会被创建。

译者注MDC 中对 strict mode 模式下 arguments 的描述有助于我们的理解,请看下面代码:

    // 阐述在 ES5 的严格模式下 `arguments` 的特性
    function f(a) {
      "use strict";
      a = 42;
      return [a, arguments[0]];
    }
    var pair = f(17);
    console.assert(pair[0] === 42);
    console.assert(pair[1] === 17);

然而,的确有一种情况会显著的影响现代 JavaScript 引擎的性能。这就是使用 arguments.callee

    function foo() {
        arguments.callee; // 使用这个函数对象
        arguments.callee.caller; // 以及这个函数对象的调用者
    }

    function bigLoop() {
        for(var i = 0; i < 100000; i++) {
            foo(); // 通常情况会作为内联函数...
        }
    }

上面代码中,foo 不再是一个单纯的内联函数 inlining译者注:这里指的是解析器可以做内联处理), 因为它需要知道它自己和它的调用者。 这不仅抵消了内联函数带来的性能提升,而且破坏了封装,因此现在函数可能要依赖于特定的上下文。

因此强烈建议大家不要使用 arguments.callee 和它的属性。

ES5 提示: 在严格模式下,arguments.callee 会报错 TypeError,因为它已经被废除了。

看完两件小事

如果你觉得这篇文章对你挺有启发,我想请你帮我两个小忙:

  1. 关注我们的 GitHub 博客,让我们成为长期关系
  2. 把这篇文章分享给你的朋友 / 交流群,让更多的人看到,一起进步,一起成长!
  3. 关注公众号 「IT平头哥联盟」,公众号后台回复「资源」 免费领取我精心整理的前端进阶资源教程

JS中文网是中国领先的新一代开发者社区和专业的技术媒体,一个帮助开发者成长的社区,目前已经覆盖和服务了超过 300 万开发者,你每天都可以在这里找到技术世界的头条内容。欢迎热爱技术的你一起加入交流与学习,JS中文网的使命是帮助开发者用代码改变世界

results matching ""

    No results matching ""