我经常在函数中有可选参数,但一些测试显示它们在 Firefox 和 safari (70-95%) 中对它们的性能造成了巨大影响。奇怪的是,如果我传入未定义的字面值,那么就没有惩罚。这里会发生什么?我不会认为这是一个作用域链问题,因为它们本质上是函数本地的。我要开始将undefined传递给每个可选参数吗?
jsPerf:http: //jsperf.com/function-undefined-args/2
我经常在函数中有可选参数,但一些测试显示它们在 Firefox 和 safari (70-95%) 中对它们的性能造成了巨大影响。奇怪的是,如果我传入未定义的字面值,那么就没有惩罚。这里会发生什么?我不会认为这是一个作用域链问题,因为它们本质上是函数本地的。我要开始将undefined传递给每个可选参数吗?
jsPerf:http: //jsperf.com/function-undefined-args/2
对于这样的功能:
function threeArgs(x, y, z) {
return x + y + z;
}
就是这样称呼的:
threeArgs(1, 2, 3);
优化器可以自由选择不生成任何代码。它很容易确定没有副作用,因为该函数只是引用其参数值并返回简单表达式的结果。由于返回值被忽略,运行时根本没有理由做任何事情。
除此之外,如果代码是:
something += threeArgs(1, 2, 3);
优化器可能决定生成大致相当于:
something += 6;
为什么?因为调用是用数字常量进行的,它可以在代码生成时安全地折叠它们。这可能是保守的,因为数字很奇怪,但这里它们都是整数,所以它可以很好地做到这一点。即使没有,它也可以安全地内联函数:
something += 1 + 2 + 3;
但是,当缺少参数时,优化器可能会退出并生成真正的函数调用。对于这样一个简单的函数,函数调用的开销很容易导致性能上的巨大差异。
通过在测试中使用变量而不是常量,并且通过实际使用函数的返回值,您可以“混淆”优化器并防止它跳过调用或预先计算结果,但您不能阻止它内联。由于这个原因,我仍然认为您的结果很有趣:它暴露了这样一个事实,即(无论如何)这些优化器对调用函数的方式很敏感。
我认为可以解释性能差异的原因是参数传递给函数对象的方式:通过arguments
对象。当不传递任何参数时,JS 将首先扫描任何给定参数的 arguments 对象,当这些参数未定义时,arguments
原型链将被扫描,一直到Object.prototype
. 如果这些都缺少所需的属性,JS 将返回undefined
. 然而,显式传递 undefined ,将其设置为直接在 arguments 对象上的属性:
function foo(arg)
{
console.log(arguments.hasOwnProperty('0'));
}
foo();//false'
foo('bar');//true
foo(undefined);//true
我认为这就是为什么明确传递未定义往往更快的原因。