javascript函数和参数对象,是否涉及成本

时间:2011-03-16 12:55:28

标签: javascript performance

在网络和框架中看到类似的代码是很常见的:

var args = Array.prototype.slice.call(arguments);

这样做,你将参数Object转换为真实的Array(无论如何JS都有真正的数组),它允许你在Array原型中使用的任何数组方法都被应用它等等。

我记得在某处读取直接访问arguments对象的速度明显慢于数组克隆或明显选择的命名参数。是否有任何事实,在什么情况下/浏览器会导致性能损失?你知道的关于这个主题的任何文章?

更新来自http://bonsaiden.github.com/JavaScript-Garden/#function.arguments的有趣发现,这使我之前阅读的内容无效...希望这个问题可以从撰写本文的@Ivo Wetzel这样的人那里获得更多答案。

在该部分的底部,它说:

  

表现神话和真相

     

始终创建arguments对象   只有两个例外是   声明为名称的情况   在函数内部或其中一个   形式参数。不要紧   是否使用。

这与http://www.jspatterns.com/arguments-considered-harmful/冲突,后者声明:

  

但是,使用它并不是一个好主意   论证原因:

     
      
  • 表现
  •   
  • 安全
  •   
     

每次调用函数时都不会自动创建arguments对象,JavaScript引擎只会按需创建它(如果使用的话)。而且这种创造在性能方面并不是免费的。使用参数与不使用参数之间的差异可能会慢1.5到4倍,具体取决于浏览器

显然,不能两者都是正确的,那么它是哪一个?

ECMA顽固的Dmitrty Soshnikov说:

  

究竟是“JavaScript引擎”   是什么意思?你在哪里得到的确切   信息?虽然,在一些人中可能是真的   实现(是的,它是好的   优化所有需要的信息   解析时可以使用上下文   代码,所以没有必要创建   如果找不到参数对象   解析),但如你所知   ECMA-262-3声明,即论证   每次时都会创建对象   进入执行环境。

4 个答案:

答案 0 :(得分:4)

Here's一些q& d测试。使用预定义的arguments似乎是最快的,但这样做并不总是可行的。如果函数的arity事先是未知的(因此,如果函数可以或必须接收可变数量的参数),我认为调用Array.prototype.slice一次将是最有效的方式,因为在那种情况下{{3使用arguments对象是最小的。

答案 1 :(得分:3)

arguments有两个问题:一个是它不是真正的数组。第二个是它只能包含所有参数,包括明确声明的参数。例如:

function f(x, y) {
    // arguments also include x and y
}

这可能是最常见的问题,您希望其余参数,而不是xy中已有的那些,所以你想要有类似的东西:

var rest = arguments.slice(2);

但你不能,因为它没有slice方法,所以你必须手动应用Array.prototype.slice

我必须说我还没有看到只是为了性能而将所有参数转换为实数组,只是为了方便调用Array方法。我必须做一些分析才能知道什么实际上更快,并且它也可能更快地依赖于什么,但我的猜测是,如果你不想在这种情况下调用Array方法,那就没有什么区别了您别无选择,只能将其转换为真实数组,或使用call或apply手动应用方法。

好消息是,在新版本的ECMAScript(Harmony?)中,我们能够写出这样的内容:

function f(x, y, ...rest) {
   // ...
}

我们将能够忘记所有这些丑陋的解决方法。

答案 2 :(得分:2)

我会反对接受的答案。
我编辑了测试,见这里:http://jsperf.com/arguments-performance/6
我将slice方法的测试和内存复制的测试添加到预分配的数组中。后者在我的计算机中的效率要高出许多倍。
正如您所看到的,该性能测试页面中的前两个内存复制方法很慢,不是由于循环,而是由于push调用。
总之,slice似乎是使用arguments的最差方法(不计算push方法,因为它们的代码甚至不比更高效的方法短得多预分配方法)。
也可能感兴趣的是,apply函数表现得非常好,并且没有太多的性能损失。

首次现有测试:

function f1(){
    for(var i = 0, l = arguments.length; i < l; i++){
        res.push(arguments[i])
    }
}

添加了测试:

function f3(){
    var len = arguments.length;
    res = new Array(len);
    for (var i = 0; i < len; i++)
         res[i] = arguments[i];
}

function f4(){
    res = Array.prototype.slice.call(arguments);
}

function f5_helper(){
    res = arguments;
}
function f5(){
    f5_helper.apply(null, arguments);
}

function f6_helper(a, b, c, d){
    res = [a, b, c, d];
}
function f6(){
    f6_helper.apply(null, arguments);
}

答案 3 :(得分:2)

暂时没有人对此进行测试,所有链接都已失效。以下是一些新的结果:

AutovalidateMode.onUserInteraction

我在这里测试了这些:https://jsben.ch/li38Ffunction loop(){ var res = [] for(var i = 0, l = arguments.length; i < l; i++){ res.push(arguments[i]) } } function loop_variable(){ var res = [] var args = arguments for(var i = 0, l = args.length; i < l; i++){ res.push(args[i]) } return res } function slice(){ return Array.prototype.slice.call(arguments); } function spread(){ return [...arguments]; } function do_return(){ return arguments; } function literal_spread(){ return [arguments[0],arguments[1],arguments[2],arguments[3],arguments[4],arguments[5],arguments[6],arguments[7],arguments[8],arguments[9]]; } 等等。这是我在 Ryzen 2700X 上的测试结果:

<头>
Firefox 84.0 铬 87.0
do_return(0,1,2,3,4,5,6,7,8,9) 98% 100%
do_return 81% 96%
loop_variable 54% 36%
spread 80% 96%
loop 100% 99%
literal_spread 77% 87%