是的,创建一个新的函数实例确实有一些可衡量的开销。这种开销是否会对实际代码产生任何影响完全是另一回事。
例如,考虑这个简单的基准测试,它以各种或多或少低效的方式切换布尔标志:
var suite = new Benchmark.Suite;
var flag = true;
suite.add('baseline', function() {
flag = !flag;
});
suite.add('local helper function', function() {
var f = function(x) { return !x };
flag = f(flag);
});
var g = function(x) { return !x };
suite.add('external helper function', function() {
flag = g(flag);
});
console.log('Running benchmark on ' + Benchmark.platform.description );
suite.on('cycle', function(event) { console.log(' - ' + event.target) });
suite.on('complete', function() { console.log('Fastest is ' + this.filter('fastest').map('name')) });
suite.run({ 'async': true });
<script src="https://cdn.jsdelivr.net/g/lodash@4.15.0,platform.js@1.3.1,benchmarkjs@2.1.1"></script>
当我在 Chrome 52 上运行此基准测试时,我得到以下结果:
Running benchmark on Chrome 52.0.2743.116 32-bit on Windows Server 2008 R2 / 7 64-bit
- baseline x 75,600,376 ops/sec ±0.90% (62 runs sampled)
- local helper function x 5,912,099 ops/sec ±0.36% (63 runs sampled)
- external helper function x 74,912,931 ops/sec ±1.05% (62 runs sampled)
Fastest is baseline,external helper function
有点令人惊讶的是,基线代码和调用外部函数来切换变量的版本几乎同样快;差异在统计上并不显着(即它与随机噪声实际上无法区分),benchmark library 报告两者并列第一。 Chrome 的 JS 引擎似乎能够内联外部辅助函数,使两个版本实际上相同。然而,在每次迭代中创建和调用新辅助函数实例的版本几乎要花费 13 倍的时间,这反映了创建和调用函数的开销。
也就是说,13 倍的开销与切换真/假标志的琐碎操作进行了比较。从某种角度来看,让我们看看如果我在基准代码中添加一个简单的 DOM 查询会发生什么,以模拟一个更真实的用例:
var suite = new Benchmark.Suite;
var flag = true;
suite.add('baseline', function() {
var foundElements = document.querySelectorAll("*");
flag = !foundElements;
});
suite.add('local helper function', function() {
var f = function(x) { return !x };
var foundElements = document.querySelectorAll("*");
flag = f(foundElements);
});
var g = function(x) { return !x };
suite.add('external helper function', function() {
var foundElements = document.querySelectorAll("*");
flag = g(foundElements);
});
console.log('Running benchmark on ' + Benchmark.platform.description );
suite.on('cycle', function(event) { console.log(' - ' + event.target) });
suite.on('complete', function() { console.log('Fastest is ' + this.filter('fastest').map('name')) });
suite.run({ 'async': true });
<script src="https://cdn.jsdelivr.net/g/lodash@4.15.0,platform.js@1.3.1,benchmarkjs@2.1.1"></script>
这一次,结果是这样的:
Running benchmark on Chrome 52.0.2743.116 32-bit on Windows Server 2008 R2 / 7 64-bit
- baseline x 898,041 ops/sec ±25.49% (39 runs sampled)
- local helper function x 605,107 ops/sec ±30.57% (47 runs sampled)
- external helper function x 720,695 ops/sec ±34.60% (38 runs sampled)
Fastest is baseline,external helper function
是的,排名还是一样(虽然时间上的差异要高得多),但是现在使用内部辅助功能的版本只比使用外部辅助功能的版本慢 20% 左右。 (这仍然超出了我的预期;显然,querySelectorAll() 非常快,至少在像 sn-p 这样的非常简单的文档上是如此。)如果您添加更多代码来实际做某事有了这些 DOM 元素,相对差异会变得更小。
所以是的,如果您可以选择在另一个调用它的函数内部或外部定义您的辅助函数(并且它本身被重复调用),没有其他理由更喜欢一个选项而不是另一个 ,那么将定义保留在外部以避免重复创建函数对象(并使 JS 引擎的内联更容易)会更有效一些。但在实践中,除非您发现这个嵌套函数正是代码中对性能至关重要的部分,否则您可能不应该担心它。