Let’s Write Fast JavaScript
A few interesting JavaScript benchmarks and tips
JavaScript is the most popular language as of now, it’s used for a wide variety of things — creating websites, servers, games, operating systems, robots, and a hell of a lot more.
But let’s be honest, even with it’s crazy popularity, it’s not as fast as it should be. Yes it’s improving, but wait for it to catch up with native everywhere. I mean, it might not be as slow on desktop, but make a hybird application and you’ll have to use hundreds of shitty tricks just to make it usable. I had this experience.
Firefox has the fastest JavaScript interpreter, SpiderMonkey. Check out Are We Fast Yet for a more details and comparisons surrounding different engines and benchmarks.
There are various efforts to make JavaScript faster, one of them is asm.js which compiles C/C++ code to JavaScript with a lot of optimizations, compiled codes look ugly, that’s why you can’t write optimized code yourself, but they run really fast. I really recommend you to check out Alon Zakai’s Big Web App? Compile It! slides.
Shut up and show me the tips!
Alright, so we want to write faster JavaScript code, here are a few small tips on making your code faster, and more memory efficient. Please note, I’m not strictly talking about DOM and web apps, it’s about JavaScript, DOM included.
Seeing is believing, I’m going to add a jsperf test case for every tip, and test it using Firefox 38 and Chrome 39.
#1 Don’t make me typecast
JavaScript is dynamically typed, but try not to use that feature if you want speed, seriously. Try to keep variables’ types consistent. It also applies to arrays, try not to mix different types in an array, althought it’s mostly optimized by browsers. That’s one of the reasons why C/C++ code compiled to JavaScript is fast, static types.
{
var x = '2';
var y = 5;
return parseInt(x, 10) + y;
}
Bonus: String to Number conversion
Let’s say you must convert a string to a number, are parseInt & parseFloat the best ways of doing it? Let’s see.
// Different ways of parsing integer/float from strings
parseFloat("100", 10)
+"100"
// integer only
parseInt("100", 10)
"100"|0
"100" >> 0
"100" << 0
//Only works for positive numbers
"100" >>> 0
parseInt test ~ parseFloat test
Firefox runs bitwise operators a lot faster, about 99% faster than parseInt and + operator. But Chrome shows no love for bitwise operators, they’re at best ~62% slower than parseInt.
parseFloat is faster than + operator on both browsers (28% on Firefox, 39% on Chrome).
So it varies on your use case, are you writing Node / Chrome Applications or Firefox Applications? I think for general purpose, parseInt is the right one to use.
#2 Don’t re-structure objects
Re-structuring objects is not cheap, follow these tips to avoid it:
Don’t use delete operator
Delete operator is a lot slower than assigning null to a property. Assigning null is 99% faster on both browsers, because it doesn’t modify an object’s structure, but delete does.
Don’t add properties later
Try not to add properties later, best to define your object’s schema from the beginning. It’s 100% faster on Firefox and 89% faster on Chrome.
dynamic properties vs pre-defined structure
#3 Use += for strings
String concatenating is a pretty expensive operation, but what’s the best way of doing it? Certainly not Array.prototype.join.
The += operator seems to be a lot faster than +, String.prototype.concat and Array.prototype.join on both browsers. Array.prototype.join is slowest, as expected.
#4 Use The Right RegExp Method
It doesn’t make sense to use RegExp.prototype.exec when you want to test against something, does it?
Yet there are performance differences between RegExp.prototype.test and String.prototype.search, let’s see which methods are faster:
RegExp.prototype.exec is a lot faster than String.prototype.match, but that’s because they are not exactly the same thing, they are different, their difference is out of this article’s scope, see this question: Stack Overflow.
In searching for existence RegEx.prototype.test is faster, probably because it doesn’t return the index of found match. String.prototype.search should only be used to find the index of desired match.
Yet you should not use RegExps to find specific string’s index inside another string, there’s a method for that, String.prototype.indexOf.
String.prototype.search vs String.prototype.indexOf
Another interesting benchmark is String.prototype.indexOf vs RegExp.prototype.test, I personally expected the latter to be faster, that’s what happens in Firefox, but in Chrome, it’s not. RegExp.prototype.test is 32% faster on Firefox, while String.prototype.indexOf is 33% faster on Chrome. In times like this, go with the one you like most, i think.
#5 Declare & Pass Local Scoped Variables
When you call a function (for example), the browser has to do something called scope lookup, which is expensive based on how many scopes it has to lookup. Try not to rely on global/higher scoped variables, instead, make locally scoped variables and pass them to functions. Less scopes to lookup, less speed to sacrifice.
This test shows us that passing variables to functions and using them from local scope is faster than looking up higher scopes to find a variable, both in Chrome and Firefox.
internal scope vs higher scope vs global
#6 You don’t need jQuery for everything
Most developers use jQuery to do the most simple tasks, I mean you don’t have to use jQuery because it’s included in your project, do you think using $val() is always necessary? Take this example:
$('input').keyup(function() {
if($(this).val() === 'blah') { ... }
});
This is one of the most important reasons to learn how to modify the DOM using vanilla JavaScript, this way you can write more efficient code.
Writing the same condition in vanilla JavaScript is 100% faster, as proven by this JSPerf Test.
$('input').keyup(function() {
if(this.value === 'blah') { ... }
});
Bonus Tip: Use Web Workers for Heavy Tasks
If you have heavy calculations in your app, let’s say, some image processing, you’d better use Web Workers to let the browser run the task in a background thread and give you the results asynchronously instead of hanging up and annoying the user.
You can also use the message channels to show the progress of the task, let’s say you have a long for loop (iterating over pixels for example), you can send a message to indicate how much of the loop is done each 100 iterations and show a progress bar so the user knows what’s happening.
That’s it, seven tips and eleven benchmarks to help you write faster JavaScript. It’s not all about performance tricks, it’s about understanding how things work in JavaScript to take your programming skills a level further.
If you found these tips helpful, please recommend and share so others can learn about these tips, too!
Have any suggestions about these tips or just want to say hello? Follow me on Twitter, I’d be glad to have a discussion with you.