JavaScript Optimization, for Novices

When I started looking around the Internet this week for ways to make sure my JavaScript wasn’t too slow, I was reminded of something about online programming resources. There are often many great tutorials and docs out there for people who are really just starting, or people who have been at it for a while — but usually nothing in between. Resources either assume you have years of experience or none at all; you either have your hand held, or find yourself thrown into a wilderness of foreign concepts. In a small effort to rectify this situation, and to simply learn things for my own sake, I’ll be writing this post about three simple recommendations for JavaScript speed and the concepts behind them.

  1. Set Object Properties and Functions on the Object’s Prototype

If you have some experience setting up classes in an object-oriented language like Python or Ruby, you understand that you write all your instance methods inside the class definition. Here’s the timeless example of the Person class, written in Ruby:

As I understand it, the code inside #initialize will set a ‘name’ instance variable for any instance of Person that you create, and when you call a method like #say_name the Person class is scanned for the method and used with the instance variables.

When you move from something like Ruby over to JavaScript, you discover that JavaScript has something called a constructor function, which at first glance looks quite similar to a class definition, and you can use it like so:

The main surface difference is that you use the JS keyword ‘this’ to define properties on the object at the time of its creation, instead of writing instance method definitions like in Ruby. This, though it seems harmless, is apparently a quite expensive way to make instances of an object in JavaScript. A constructor function in JavaScript is not a blueprint like a class; it’s just a function that says what properties to set on the newly created object. This means that each time you use the ‘new’ keyword to create a new Person object, you set the sayName function on just that object. In Ruby, this is akin to defining your instance methods like this:

In this case, no other instance of Person will know how to say its name unless you explicitly tell it so.

JavaScript doesn’t have class inheritance like Ruby and Python do, but it does have something vaguely similar. JavaScript uses object prototypes, which essentially means that each time you make a new object you actually make a clone of the prototype of that kind of object. (For a very good and very in depth explanation of this, go here.) This means that every instance of Person you churn out will first take all the properties of the prototype Person object, before it has all its own unique properties set. And, good news, you can access this prototype object and set properties on it like so:

Here I’ve set a few more properties on the prototype, just to illustrate that you can use this feature to set properties and functions that you want to be common across all Person objects. Inside of sayName, I’ve used ‘this’ to refer to the object currently being sent the message to call its sayName function. This method of setting properties on the prototype means that you can save time (during object construction) and memory space (because the properties will only be saved in one place).

2. Avoid Creating Nested Closures

If you don’t know what a closure is, I’ll try to provide a simple example and a little bit of an explanation. I’m not much of an expert on the subject, though, so definitely do more research on your own to get a more thorough understanding of how they work, if my explanation is not quite enough. I remember how strange this concept was when I first came across it, so take a little while to let it sink in.

A closure is, essentially, a function that can be passed around as an argument or a variable before it has been called, and which stores references to all variables in or above its scope, even if those variables are no longer being used by other parts of the program. (If you’re thinking to yourself, “But that’s the definition of all functions in JavaScript,” you’re ahead of the curve — but people tend to only refer to functions as ‘closures’ when they actually make use of this special feature.) Closures were explained to me using the following example, in which one function returns another function that increments a counter defined within the first function:

Strange, right? At first, there are two puzzling things about this: that the variable ‘counter’ can be a function, and (the most puzzling part) that the function has access to the ‘count’ variable, which seems like it should have been lost to oblivion because its context is no longer in use. The first is a main feature of JavaScript, borrowed from functional programming languages. When you call the first function, it simply returns a function definition, which, if you’ve saved it somewhere, you can also call. The second is taking advantage of the fact functions in JavaScript remember all of the variables around them, and can update them as they please — which is why ‘count’ continues to go up and up.

Another way that people usually explain closures is that you can use them to create a kind of “function factory,” a function that creates other customized functions. Here’s a trivial one that creates functions that carry out the basic arithmetic operators:

As you can see, each version of mathFunc that we call ‘remembers’ what the variable ‘operator’ was in its own context, and uses that to operate differently on the same arguments.

It doesn’t take an especially creative mind to imagine why this feature of JavaScript might have seriously detrimental side effects. As programs get bigger, they become much more complex, and as they become more complex, they have much more data to handle; in JavaScript, this means that each new closure that you create (by defining functions within functions within functions) has the burden of remembering not only all of its own scope’s variables but also all the variables of each level of scope above it. (Note: there’s a new feature of the ES6 specification that lets you restrict variables to only the scope they were defined in, to help you manage this complexity.) So, closures are a powerful feature of JavaScript, but they are costly and not to be used without forethought.

3. Avoid Lengthy Scope Chain Searches

This piece of advice is intimately related to number 2. above. If each new level of scope creates with it the memory of all of the variables the scopes above it, you can of course access any variable from any higher level of scope from a lower level. But this can also be costly. If you’re familiar with how method lookup in a class-based object-oriented language works, this will make sense to you. Essentially, when the program running your JavaScript runs into a reference to a variable, it first scans all of the variables in its local scope to see if anything matches. If it finds a match, it retrieves the value. If it doesn’t find a match, the same process repeats at the next level. Here’s some code that (again, trivially) demonstrates this:

The greater the difference between current level and level in which the variable was originally defined, the longer the lookup time will be.

I’ve tried to express these ideas in ways that feel relatively easy and relatable, but for every abstraction I’ve used, there thousands of real, detailed implementations with quirks and features and problems that I can’t begin to get into. To research JavaScript’s object prototyping technique I used Yehuda Katz’s blog post on the subject (linked above), and to research closures and scope I used two posts from David Shariff’s blog, one about execution context / the execution stack and the other about scope chain / identifier resolution. I hope I’ve boiled down some of these ideas into more digestible portions and, in the process, introduced why each may be costly.

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.