As the title mentions. Is it a smart idea to declare a function to a variable if you care about performance? For example in this scenario: I'm creating a function which will be called x times repeatedly. This function contains a pair variables and doesn't accept arguments. One of this variable contains an anonymous function, and this variable / function will be called everytime the parent has been called.
So basically the structure would look like this:
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();
}
}
Am I better off declaring this function as a named / seperated function? Or is this the way to go? (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)
Yes, creating a new function instance does have some measurable overhead. Whether this overhead makes any difference in real code is another matter entirely.
For example, consider this simple benchmark that toggles a boolean flag in various more or less inefficient ways:
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/[email protected],[email protected],[email protected]"></script>
When I run this benchmark on Chrome 52, I get the following results:
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
A bit surprisingly, the baseline code and the version that calls an external function to toggle the variable are almost equally fast; the difference is not statistically significant (i.e. it's effectively indistinguishable from random noise), and the benchmark library reports both as tied for first place. It seems that Chrome's JS engine is able to inline the external helper function, making the two versions effectively identical. The version that creates and calls a new helper function instance on every iteration, however, takes almost 13 times as much time, reflecting the overhead of creating and calling the function.
That said, the 13-fold overhead is compared to the trivial operation of toggling a true/false flag. For some perspective, let's see what happens if I add a simple DOM query into the benchmark code, to simulate a slightly more realistic use case:
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/[email protected],[email protected],[email protected]"></script>
This time, the results look like this:
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
Yes, the ranking is still the same (although the variance on the timings is much higher), but now the version with the internal helper function is only about 20% slower than the one using an external helper function. (Which is still more than I expected; apparently, querySelectorAll()
is pretty fast, at least on a really simple document like this snippet.) And if you added some more code to actually do something with those DOM elements, the relative difference would become even smaller still.
So yes, if you have a choice between defining your helper function inside or outside another function that calls it (and is itself called repeatedly), with no other reason to prefer one option over the other, then it's a bit more efficient to keep the definition outside to avoid repeated creation of function objects (and to make inlining easier for the JS engine). But in practice, unless you find out that this nested function is exactly the performance-critical part of your code, you probably shouldn't worry about it.