High Performance JavaScript (Book Review)
When I saw on NCZ’s blog that he was writing a new book on JavaScript performance techniques, I instantly went to pre-order it. Having partially read through High Performance JavaScript by now, I figured I’d start writing a review of this excellent book.
Since JavaScript is such an expressive language, there are dozens of different ways to do the same thing. Some of them good, some mediocre, and a lot of them bad. It’s amazing how much awful JS info is on the web, all leftover from the dark ages of JS (‘96 - ‘05). Up until this point, we haven’t had an authoritative source on the topic of how to write JavaScript that performs well, both in and out of the browser. Sure we’re had great books about web performance (High Performance Web Sites is my favorite), but we haven’t had anything specific to JavaScript. Now we do.
Nicholas is widely known as one of the best minds in the JavaScript world today. He joined Yahoo! in 2006 as a front end engineer and has been working on one of the most trafficked pages on the interwebs, the Yahoo! home page. His blog (nczonline.net) is a treasure trove of information on all things JavaScript & web performance. Some recent gems include Interviewing the front-end engineer & Writing maintainable code. It goes without saying that he knows his stuff when it comes to JavaScript & performance. As his books and blog posts have shown, he’s also a very skilled technical writer, keeping topics fresh, concise, & relevant.
I’m writing this as I read along, so the verbosity of this post is due to me taking reference notes on interesting things as I go.
Chapter 1: Loading & Execution
Nick doesn’t waste any time getting into what the reader wants, fresh tips! Right away we begin to learn the specifics of how browsers react depending on where & how you include your JS. There are many ways that work, but few ways that work well.
Specifically:
- Why is it important to put your <script> includes just above the closing </body> tag?
- What is the browser doing while loading those external files?
- Why should you put all your in-page JS code above your CSS includes? (A: If you put it after a </link> tag referencing an external stylesheet, the browser will block execution while waiting for that stylesheet to download)
- How you can use the defer attribute in <script> tags to delay non-essential execution of code.
- A thorough look at dynamic script loading to import & execute your JS without blocking the browser.
- An overview of some of the common JS loaders used today (YUI3, LazyLoader, & LABjs).
While much of the content in this chapter contains common knowledge among experienced developers, it is important knowledge to cover as it serves as the foundation for the rest of the book. Don’t worry, we’ll get more advanced.
Chapter 2: Data Access
Here’s where the sexy parts come into play; diagrams, graphs, and benchmarks! This second chapter is where you’ll learn about how exactly the JS engine accesses data depending on how you store it. The steepest learning curve within JavaScript for beginning developers is understanding variable scope. This is the first time I’ve ever come across an explanation of JavaScript’s [[Scope]] property, now all the scoping & speed issues make perfect sense!
Major topics covered in this chapter:
- Why do global variables perform so slowly?
- Why creating data as local variables as opposed to object properties is 10%-50% faster (depending on the browser).
- Why is it a good idea to create local instances of global variables?
- Why with, try/catch, and eval are bad ideas from a performance perspective. (A: they augment the scope by inserting themselves first on the tree)
- What truly happens under the hood when a variable is found to be undefined?
- Closure scope and why they can cause memory leaks.
- How prototype’s work and performance issues related to traversing up the prototype chain.
- Why is it bad to use deeply nested object members (i.e. foo.bar.baz.bop())?
There were so many “Ah hah! I get it now!” moments in this chapter for me that it alone was worth the price of the book. It took me about 5x as long as it should have to get through this chapter because I was too busy playing with Firebug as I began to learn some of these concepts.
Chapter 3: DOM Scripting
This book contains a few guest author chapters, and this is one of them. In this chapter we learn about DOM scripting by another Yahoo, Stoyan Stefanov.
Many web developers don’t understanding what exactly “DOM scripting” is, even though they likely do it on a daily basis. Many could tell you what the acronym stands for and that it represents the structure of an (X)HTML/XML document, but most don’t know that it also represents the API part of how you interact with the document. When you are using document.getElementById(“foobar”) or myelement.style.color = “blue”, you are utilizing a DOM API function accessible via JavaScript, but it has nothing to do with the ECMAScript (aka: JavaScript) standard.
This chapter is chalk-full of great best practices & overviews of DOM principles. The first thing we learn is that accessing the DOM is so slow because we’re crossing the bridge between JavaScript and native browser code. Jumping between the two is expensive, and should be kept to a minimum. There are a lot of tricks & tips that are very under-utilized by most developers when DOM scripting.
For example:
- Using the non-standard innerhtml is way faster than creating nodes via the native document.createElement() method.
- When looping through a NodeCollection you should cache the length of the node in a local variable because it’s own length property is very slow.
- Iterating through nextSibling() can be 100x faster than using childNodes()
This chapter also goes into a detailed explanation of what repaint & reflow are, when they occur, and how understanding them will improve your application performance. The realization I had after reading the R&R explanation is we do stupid stuff all the time simply because we don’t understand how the browser renders and updates our pages. You know how you’ve always heard using margin-left & margin-right as separate styles is a bad idea? Well, here you find out why. Oh, and did you know there was a cssText property you can use to batch your CSS modifications? I didn’t.
As JS libraries get more & more popular, knowledge of good DOM scripting is becoming increasingly rare. Take event delegation for example. Many developers just presume jQuery’s live() or YUI3’s delegate() methods are just magic. They’re far from it, and are actually easy to understand concepts. When interviewing candidates for front end jobs at Yahoo!, this is one of the primary concepts we expect candidates to understand. They may have never used it, but the good ones will figure it out as they are whiteboarding and we walk them through the challenges.
JS libraries are awesome, but it’s because they abstract out the cross-browser differences & fix a flawed language, not because they allow you to forget what it actually going on under the hood.
Chapter 4: Algorithms & Flow Control
Chapter 4 kicks off with a quick overview of the 4 different types of loops in JavaScript (while, do-while, for, for-in). The first 3 have equivalent performance, but for-in is the one to watch out for and should only be used when iterating an unknown number of elements (i.e. object properties). We then learn about important concepts like length caching and various other optimization techniques focused on reducing the number of operations per iteration.
Next up are conditionals, such as if-else and switch. We learn when it is appropriate to use each one, and when they can be ditched for a much faster method, like using arrays as lookup tables.
Finally we come to the topic of recursion. We skip the basics of “What is recursion” and jump straight into browser limitations with call stacks and advanced recursion topics such as memoization to cut out the fat in your stack.
Since the majority of our time spent coding is inside of loops, conditionals, and (if we really want to optimize) recursion, this chapter has great, basic information on efficient shortcuts that will set you apart from the other developers on your team. Techniques learned in this chapter extend beyond the scope of JavaScript and can be used in just about every other programming language.
Chapter 5: Strings and Regular Expressions
Another guest author chapter, this time by regex guru Steve Levithan
Along with loops, another very common task in JavaScript is string manipulation, most commonly one by concatenation or regular expressions, so it makes sense to have a whole chapter to itself.
When most people start out with JS, their concatenation method is likely var str = “foo”; str = str + “bar”; //str = “foobar”, then they discover the += operator and it becomes var str = “foo”; str += “bar”; //str = “foobar”. It turns out that one of those is more efficient when it comes to memory usage, and it happens to not be the latter. This chapter provides some memory allocation table diagrams to explain the difference and how different browsers perform that operation. It should also be noted that another alternate method of concatenation, [‘foo’,’bar’].join(‘’); is the preferred method in IE 6 & 7, so that should be considered depending on your userbase.
The second half of this chapter covers regular expressions, which usually make me cringe. I have no problem writing them, but they’re an absolute nightmare to maintain sometimes. Douglas Crockford has a saying, “If a regular expression is longer than 2 inches, find another method.” I couldn’t agree more.
In this review, I only covered the first half of the book. Here are the remaining chapters:
- Chapter 6: Responsive Interfaces
- Chapter 7: Ajax
- Chapter 8: Programming Practices
- Chapter 9: Building and Deploying high performance JavaScript applications
- Chapter 10: Tools
If you like what you’ve seen so far, go buy it!