JavaScript - 将函数声明为变量是个聪明的主意吗?
JavaScript - is it a smart idea to declare a function to a variable?
如标题所述。 如果您关心性能,那么向变量声明一个函数是不是一个聪明的主意?例如在这种情况下:我正在创建一个将被重复调用 x 次的函数。此函数包含一对变量且不接受参数。其中一个变量包含一个匿名函数,并且这个变量/函数将被调用 每次 parent 被调用。
所以基本上结构如下所示:
function myFunction() {
var foundElements = document.querySelectorAll("*"),
updateElements = function() {
console.log("Hello world, i've been called to work as a slave!");
};
if (foundElements.length > 0) {
updateElements();
}
}
我是否最好将此函数声明为命名/分离函数?或者这是要走的路? (因为我认为这种方法会导致系统在每次调用 parent 时重新创建函数,如果我错了请纠正我)
在此先致谢。
because I think this method causes the system to recreate the function
every single time the parent is called, correct me if i'm wrong
我不确定 javascript 编译器是如何工作的,但我怀疑它们是否会在每次调用 "parent" 时编译该函数。至少如果我写一个编译器,它会将编译后的函数存储在缓存中并每次都使用该缓存。
无论如何编译 javascript 函数不会导致性能问题,因为它非常便宜。访问 DOM 的成本要高得多,所以如果我是你,我会担心 document.querySelectorAll("*")
部分,尤其是 *
部分...
是的,创建一个新的函数实例确实有一些可衡量的开销。这种开销是否会对实际代码产生任何影响完全是另一回事。
例如,考虑这个简单的基准测试,它以各种或多或少低效的方式切换布尔标志:
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 倍的开销与切换 true/false 标志的微不足道的操作相比。从某种角度来看,让我们看看如果我在基准代码中添加一个简单的 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()
非常快,至少在像这个片段这样的非常简单的文档上是这样。)如果你添加更多代码来实际 做一些事情加上那些DOM元素,相对差异会变得更小。
所以是的,如果您可以选择在另一个调用它的函数内部或外部定义您的辅助函数(并且它本身被重复调用),没有其他理由选择一个选项而不是另一个选项,那么将定义保留在外面会更高效一些,以避免重复创建函数对象(并使 JS 引擎更容易内联)。但在实践中,除非您发现这个嵌套函数正是您代码的性能关键部分,否则您可能不应该担心它。
如标题所述。 如果您关心性能,那么向变量声明一个函数是不是一个聪明的主意?例如在这种情况下:我正在创建一个将被重复调用 x 次的函数。此函数包含一对变量且不接受参数。其中一个变量包含一个匿名函数,并且这个变量/函数将被调用 每次 parent 被调用。
所以基本上结构如下所示:
function myFunction() {
var foundElements = document.querySelectorAll("*"),
updateElements = function() {
console.log("Hello world, i've been called to work as a slave!");
};
if (foundElements.length > 0) {
updateElements();
}
}
我是否最好将此函数声明为命名/分离函数?或者这是要走的路? (因为我认为这种方法会导致系统在每次调用 parent 时重新创建函数,如果我错了请纠正我)
在此先致谢。
because I think this method causes the system to recreate the function every single time the parent is called, correct me if i'm wrong
我不确定 javascript 编译器是如何工作的,但我怀疑它们是否会在每次调用 "parent" 时编译该函数。至少如果我写一个编译器,它会将编译后的函数存储在缓存中并每次都使用该缓存。
无论如何编译 javascript 函数不会导致性能问题,因为它非常便宜。访问 DOM 的成本要高得多,所以如果我是你,我会担心 document.querySelectorAll("*")
部分,尤其是 *
部分...
是的,创建一个新的函数实例确实有一些可衡量的开销。这种开销是否会对实际代码产生任何影响完全是另一回事。
例如,考虑这个简单的基准测试,它以各种或多或少低效的方式切换布尔标志:
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 倍的开销与切换 true/false 标志的微不足道的操作相比。从某种角度来看,让我们看看如果我在基准代码中添加一个简单的 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()
非常快,至少在像这个片段这样的非常简单的文档上是这样。)如果你添加更多代码来实际 做一些事情加上那些DOM元素,相对差异会变得更小。
所以是的,如果您可以选择在另一个调用它的函数内部或外部定义您的辅助函数(并且它本身被重复调用),没有其他理由选择一个选项而不是另一个选项,那么将定义保留在外面会更高效一些,以避免重复创建函数对象(并使 JS 引擎更容易内联)。但在实践中,除非您发现这个嵌套函数正是您代码的性能关键部分,否则您可能不应该担心它。