在网络和框架中看到类似的代码是很常见的:
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/冲突,后者声明:
显然,不能两者都是正确的,那么它是哪一个? ECMA顽固的Dmitrty Soshnikov说:但是,使用它并不是一个好主意 论证原因:
- 表现
- 安全
每次调用函数时都不会自动创建arguments对象,JavaScript引擎只会按需创建它(如果使用的话)。而且这种创造在性能方面并不是免费的。使用参数与不使用参数之间的差异可能会慢1.5到4倍,具体取决于浏览器
究竟是“JavaScript引擎” 是什么意思?你在哪里得到的确切 信息?虽然,在一些人中可能是真的 实现(是的,它是好的 优化所有需要的信息 解析时可以使用上下文 代码,所以没有必要创建 如果找不到参数对象 解析),但如你所知 ECMA-262-3声明,即论证 每次时都会创建对象 进入执行环境。
答案 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
}
这可能是最常见的问题,您希望其余参数,而不是x
和y
中已有的那些,所以你想要有类似的东西:
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/li38F、function 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% |