What is the best compromise between performant execution and execution time consistency for method calling in javascript?
I'm still learning javascript and would use prototypes for most everything (i.e. Brendan Eich here) but I think I'm finding better performance and consistency from function closures (I know I'm probably over optimizing). One prototypal pattern I've been testing:
function PrototypeA() {}
PrototypeA.prototype.a = 0;
PrototypeA.prototype.b = 0;
PrototypeA.prototype.someMath = function() {
this.a += 1;
this.b += 2;
};
var Test = new PrototypeA();
Test.someMath();
Decided after reading this (great!) post to benchmark closures. The current closure pattern I'm drifting toward:
var SubModule = new((function() {
var a = 0;
var b = 0;
var someMath = function() {
a += 1;
b += 2;
};
return function() {
this.someMath = someMath
};
})())();
I'm finding that many benchmarks of various patterns are misleading due to errors in the test setup such as calling constructors in the benchmark or calling methods in ways that cause different scope traversal paths. I'm just trying to test method execution.
After lots of benchmarking locally (benchmark.js across many different patterns), I put up the notable patterns:
Jsperf Here
My findings seem to support that well organized closures don't deviate in execution time as much other types of objects (I use the term "type" loosely). I realize I could be completely wrong due to any number or errors on the benchmark or setup.
Thanks for looking!
Since your use case is animation, you want to stick to closures. Simple rule of thumb with javascript is "the more .
there are, the slower the code goes". In the examples that you posted on jsperf, the slowest ones are the ones that are referencing this
quite a bit. Every time you do that, the engine has to look at the current object and determine whether or not what you are trying to access is available immediately on the object, and if it has to look up the prototype chain. On the closure side of things, you're referencing a rather limited scope, predetermined by the function that is you've written, and the most the engine has to do is be able to look at that scope to reference the variables in it.
Now, there are some gotcha's. First, usage of this
is significantly faster when you're only referencing it once or twice. Not the case in this example, but it's worth mentioning. The more times you add a reference to this
, the slower prototype reliant code goes. Look at this simple perf for an example. Second, if you're constructing a lot of these modules (for example, if you're building a data grid), construction is incredibly slow with closures and there's a lot of memory wasted. See this example for the speed difference. The more methods and properties you add, the worse it gets. That's because creating a new closure each time makes unique functions and properties, whereas the prototype based one just reuses the same stuff.
In either case, my humble recommendation is to really just look at the situation you're programming for. Most of the time, using prototypes is the way to go - readability [in some cases], re-usability, extendability.. but if they're just too slow, then you just can't use them. That being said, there's nothing wrong with combining the two concepts to give you a reusable and modular setup for methods that don't get hit as often. For instance, try using 'Mixins':
var Mixin = {
interval: 100,
finish: function () { window.clearInterval(this.intervalRef); },
start: function (callback) { this.intervalRef = window.setInterval(callback, this.interval)
};
var Module = (function () {
function getMessage () {
return "weee!";
}
var somethingThatReallyNeedsSpeed = function () {
var iterations, self = this;
this.start(function () {
console.log(getMessage());
iterations++;
if(iterations > 10000) {
self.finish();
}
});
}
somethingThatReallyNeedsSpeed.prototype = Mixin;
return somethingThatReallyNeedsSpeed;
})();
In this example, construction speed isn't hit as bad due to the prototype just being set to reference the Mixin
object - there are two methods and a property that the special function does not need to have constructed each time, but it still has available via this
.
</rant>
Scope chain lookup is generally faster than prototype chain lookup: Scope Chain Lookup vs Prototype Chain Lookup · jsPerf
A few points to remember:
prototype
of a function. It leads to unexpected problems. Only functions must be declared on the prototype
.new((function() { ... })())()
unless you want people to come after you with pitchforks and firebrands.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With