JavaScript Prototype Chains, Scope Chains, and Performance: What You Need to Know

JavaScript: More than meets the eye

JavaScript can seem like a very easy language to learn at first. Perhaps it’s because of its flexible syntax. Or perhaps it’s because of its similarity to other well known languages like Java. Or perhaps it’s because it has so few data types in comparison to languages like Java, Ruby, or .NET.

But in truth, JavaScript is much less simplistic and more nuanced than most developers initially realize. Even for developers with more experience, some of JavaScript’s most salient features continue to be misunderstood and lead to confusion. One such feature is the way that data (property and variable) lookups are performed and the JavaScript performance ramifications to be aware of.

In JavaScript, data lookups are governed by two things: prototypal inheritance and scope chain. As a developer, clearly understanding these two mechanisms is essential, since doing so can improve the structure, and often the performance, of your code.

Property lookups through the prototype chain

When accessing a property in a prototype-based language like JavaScript, a dynamic lookup takes places that involves different layers within the object’s prototypal tree.

In JavaScript, every function is an object. When a function is invoked with the new operator, a new object is created. For example:

Since JavaScript functions are objects, they can have properties. A particularly important property that each function has is called prototype.

prototype, which is itself an object, inherits from its parent’s prototype, which inherits from its parent’s prototype, and and so on. This is often referred to as the prototype chain. Object.prototype, which is always at the end of the prototype chain (i.e., at the top of the prototypal inheritance tree), contains methods like toString(), hasProperty(), isPrototypeOf(), and so on.

Each function’s prototype can be extended to define its own custom methods and properties.

When you instantiate an object (by invoking the function using the new operator), it inherits all the properties in the prototype of that function. Keep in mind, though, that those instances will not have direct access to the prototype object but only to its properties. For example:

There’s an important and somewhat subtle point here: Even if p1 was created before the getFullName method was defined, it will still have access to it because its prototype is the Person prototype.

(It is worth noting that browsers also store a reference to the prototype of any object in a __proto__ property, but it’s really bad practice to directly access the prototype via the __proto__ property, since it’s not part of the standard ECMAScript Language Specification, so don’t do it!)

Since the p1 instance of the Person object doesn’t itself have direct access to the prototype object, if we want overwrite getFullName in p1, we would do so as follows:

Now p1 has its own getFullName property. But the p2 instance (created in our earlier example) does not have any such property of its own. Therefore, invoking p1.getFullName() accesses the getFullName method of the p1 instance itself, while invoking p2.getFullName() goes up the prototype chain to the Person prototype object to resolve getFullName:

When using prototypal inheritance, remember to define properties in the prototype after having either inherited from the parent class or specified an alternate prototype.

To summarize, property lookups through the JavaScript prototype chain work as follows:

If the object has a property with the given name, that value is returned. (The hasOwnProperty method can be used to check if an object has a particular named property.)

If the object does not have the named property, the object’s prototype is checked

Since the prototype is an object as well, if it does not contain the property either, its parent’s prototype is checked.

This process continues up the prototype chain until the property is found.

If Object.prototype is reached and it does not have the property either, the property is considered undefined.

Understanding how prototypal inheritance and property lookups work is important in general for developers but is also essential because of its (sometimes significant) JavaScript performance ramifications. As mentioned in the documentation for V8 (Google’s open source, high performance JavaScript engine), most JavaScript engines use a dictionary-like data structure to store object properties. Each property access therefore requires a dynamic look-up in that data structure to resolve the property. This approach makes accessing properties in JavaScript typically much slower than accessing instance variables in programming languages like Java and Smalltalk.

Variable lookups through the scope chain

Another lookup mechanism in JavaScript is based on scope.

To understand how this works, it’s necessary to introduce the concept of execution context.

In JavaScript, there are two types of execution contexts:

Global context, created when a JavaScript process is launched

Local context, created when a function is invoked

Execution contexts are organized into a stack. At the bottom of the stack, there is always the global context, that is unique for each JavaScript program. Each time a function is encountered, a new execution context is created and pushed onto the top of the stack. Once the function has finished executing, its context is popped off the stack.

Within each execution context is a special object called a scope chain which is used to resolve variables. A scope chain is essentially a stack of currently accessible scopes, from the most immediate context to the global context. (To be a bit more precise, the object at the top of the stack is called an Activation Object which contains references to the local variables for the function being executed, the named function arguments, and two “special” objects: this and arguments.) For example:

Note in the above diagram how this points to the window object by default and also how the global context contains examples of other objects such as console and location.

When attempting to resolve variables via the scope chain, the immediate context is first checked for a matching variable. If no match is found, the next context object in the scope chain is checked, and so on, until a match is found. If no match is found, a ReferenceError is thrown.

It is important to also note that a new scope is added to the scope chain when a try-catch block or a with block is encountered. In either of these cases, a new object is created and placed at top of the scope chain:

In most other languages, the code above would lead to an error because the “life” (i.e., scope) of the variable i would be restricted to the for block. In JavaScript, though, this is not the case. Rather, i is added to the activation object at the top of the scope chain and it will stay there until that object is removed from the scope, which happens when the corresponding execution context is removed from the stack. This behavior is known as variable hoisting.

It is worth noting, though, that support for block-level scopes is making its way into JavaScript through the new let keyword. The let keyword is already available in JavaScript 1.7 and is slated to become an officially supported JavaScript keyword as of ECMAScript 6.

JavaScript Performance Ramifications

The way that property and variable lookups, using prototype chain and scope chain respectively, work in JavaScript is one of the language’s key features, yet it is one of the trickiest and most subtle to understand.

The lookup operations we’ve described in this example, whether based on the prototype chain or the scope chain, are repeated every time a property or variable is accessed. When this lookup occurs within loops or other intensive operations, it can have significant JavaScript performance ramifications, especially in light of the single-threaded nature of the language which prevents multiple operations from happening concurrently.

In this example, we have a long inheritance tree and three nested loops. Inside the deepest loop, the counter variable is incremented with the value of delta. But delta is located almost at the top of the inheritance tree! This means that each time child.delta is accessed, the full tree needs to be navigated from bottom to top. This can have a really negative impact on performance.

Understanding this, we can easily improve performance of the above nestedFn function by using a local delta variable to cache the value in child.delta (and thereby avoid the need for repetitive traversal of the entire inheritance tree) as follows:

Of course, this particular technique is only viable in a scenario where it is known that the value of child.delta won’t change while the for loops are executing; otherwise, the local copy would need to be updated with the current value.

OK, let’s run both versions of the nestedFn method and see if there is any appreciable performance difference between the two.

Note that use of local variables to avoid expensive lookups is a technique that can be applied both for property lookup (via the prototype chain) and for variable lookups (via the scope chain).

Moreover, this type of “caching” of values (i.e., in variables in the local scope) can also be beneficial when using some of the most common JavaScript libraries. Take jQuery, for example. jQuery supports the notion of “selectors”, which are basically a mechanism for retrieving one or more matching elements in the DOM. The ease with which one can specify selectors in jQuery can cause one to forget how costly (from a performance standpoint) each selector lookup can be. Accordingly, storing selector lookup results in a local variable can be extremely beneficial to performance. For example:

// this does the DOM search for $('.container') "n" times
for (var i = 0; i < n; i++) {
$('.container').append(“Line “+i+”<br />”);
}
// this accomplishes the same thing...
// but only does the DOM search for $('.container') once,
// although it does still modify the DOM "n" times
var $container = $('.container');
for (var i = 0; i < n; i++) {
$container.append("Line "+i+"<br />");
}
// or even better yet...
// this version only does the DOM search for $('.container') once
// AND only modifies the DOM once
var $html = '';
for (var i = 0; i < n; i++) {
$html += 'Line ' + i + '<br />';
}
$('.container').append($html);

Especially on a web page with a large number of elements, the second approach in the code sample above can potentially result in significantly better performance than the first.

Wrap-up

Data lookup in JavaScript is quite different than it is in most other languages, and it is highly nuanced. It is therefore essential to fully and properly understand these concepts in order to truly master the language. Data lookup and other common JavaScript mistakes should be avoided whenever possible. This understanding is likely to yield cleaner, more robust code that achieves improved JavaScript performance.

About the author

Originally from Italy, Diego spent several years working in The Netherlands before moving to Prague, in the Czech Republic. There he has been working as a senior freelance engineer in addition to working on his own company: a web-based CRM for small businesses. [click to continue...]

Comments

Alejandro Hernandez

Great article!! a little bit more on prototypal inheritance discussion
http://picanteverde.wordpress.com/2013/06/18/javascript-inheritance-and-prototype-chain-search-the-end-of-the-inheritance-discussion/

Milan Raj

The article mentions how local variable caching can be useful when doing frequent scope chain and prototype chain look-ups, but is there a significant performance difference between look-ups in the scope chain vs the prototype chain themselves?

Although not necessarily in the scope of caching, in your "Performance Ramifications" test, DOM manipulation in a loop can also be optimized. You can get better performance by concatenating your HTML in a string and then only modifying the DOM once after the loop.
I included your two examples above (1000 iterations) and added a third that only manipulates the DOM once after the loop.
Test 1: 109 milliseconds
Test 2: 89 milliseconds
Test 3: 20 milliseconds
A fiddle with all three tests: http://jsfiddle.net/x4dwR/

Guest

<blockquote>As mentioned in the documentation for V8 (Google’s open source, high performance JavaScript engine), most JavaScript engines use a dictionary-like data structure to store object properties.</blockquote>
Actually the very same documentation describes V8's approach to avoiding dictionary representation and repetitive lookups whenever possible (look for words "hidden classes" and "inline caching") and these days most good JavaScript engines (which includes all major browsers) use similar techniques. So your assumption that accessing a property through a prototype chain always involves dictionary lookups is incorrect.
Now the benchmark that you think is demonstrating universal badness of prototype lookups is demonstrating something else: inability of the optimizing compiler in the old versions of V8 to inline the load of <i>normal</i> properties defined on prototypes. Methods for example don't have such a limitation even in the older versions of V8 so if you define <code>delta</code> as method instead of property:
<pre>
function Parent() { }
Parent.prototype.delta = function () { return 10; };
</pre>
and change <code>child.delta</code> to <code>child.delta()</code> then amazingly the different in performance will disappear.
Now newer version of V8 does not have such a limitation in the optimizing compiler and hoists lookup out of the inner loop, but it hits some other performance anomalies related to how representation inference and OSR works, e.g. writing <code>'Final result: ' + counter.toString()</code> in the newer V8 helps to work around the issue with non-optimal representation selection for the counter in the inner loop (tagged representation selected, but the unboxed double should be preferred instead).
Overall I would like to strongly advice against microbenchmarking things like that. At least when benchmarking invoke the function in question several times, though it is just a tip of the iceberg. You really need to look under the hood to see what is really happening and why.
[I would also like to note some typos in the body of the post:
picture http://www.toptal.com/uploads/blog/image/384/toptal-blog-image-1398995628687.png is inaccurate in describing the first chain (should be null, not undefined) and the last chain makes no sense it should not be any different from second before last. Judging from the presence of <code>Entity.prototype</code> I suspect intent was to show how <code>new</code> operator works, but then it should be made visible and <code>Function.prototype</code> should not be in the chain
<code>p1.getFullName() = function(){</code> should be <code>p1.getFullName = function(){</code>
etc.
]

Vyacheslav Egorov

The comment above is mine, I am not sure how I managed to post it as Guest.

diegocastorina

The docs say: "most JavaScript engines use a dictionary-like data structure to store object properties."
Please note the word "most": the fact that V8 does not behave like that does not mean that you can ignore the rest of the world!
This article is about Javascript and not V8. Considering how many Javascript VM implementations, running on different platforms and devices, there are, you cannot base yourself just on just one.
Your code will be running on all kind of devices and it has to be reasonably quick on all of them.
If you want to base yourself on one VM, than you should take the slowest and not the fastest one.

H. Singer

Great catch. Thanks. Just corrected this.

diegocastorina

It all depends on the VM implementation and you cannot make any assumption on it. However, your main focus has to always be writing the best code possible. Optimization should be done only after performance issues arise. Remember: "premature optimization is the root of all evil" :)

Vyacheslav Egorov

That page in the V8 documentation was written quite a while ago and as I said already these days most engines besides V8 apply the same optimization techniques as V8, which means objects are <b>not</b> represented as dictionaries whenever possible. What V8 calls <i>hidden classes</i> JavaScriptCore (engine inside Safari) calls <i>structures</i> and SpiderMonkey (engine inside Firefox) calls <i>shapes</i>; I don't know who Chakra (IE engine) team calls them internally, but they still use them. Understanding these fundamental optimization techniques, their strength and weaknesses is the key to writing high performance JS code.
Now there is no such thing as an abstract JavaScript performance. You note this yourself. However at the same time you decided to run a single microbenchmark on a single platform to prove your point. I happen to know V8 pretty well, so I am simply trying to explain you what is <i>really</i> going on there, when you run your code on <code>node</code>; what V8 can and can't do, so that you can adjust your mental model at least for V8. Obviously my comment does not apply to other engines directly, though I would expect both JSC and SM show similar optimization capabilities to V8 as there is fundamentally nothing preventing them from doing this and these days both posses sophisticated optimization pipelines.

diegocastorina

This post is about how data look-up works in Javascript. Among other things, it shows how it can have impacts also on performance but that's not the main subject treated and for this reason I wrote that simple example and run it just on Node.
I totally agree with the fact that a single benchmark run on a single platform is not reliable.
If I wanted to cover in depth performance implications of property look-up I would have written a separate article just for that, I would have tested a more sophisticated example and I would have run on it on different VM and multiple OS.

Vyacheslav Egorov

I guess the main point I am trying to bring across here is that you should not use such a simple example at all, because in some sense it encourages wrong approach to measuring performance and wrong approach to interpreting the results.
In this particular case, I feel, that it also kinda promotes the assumption that there is something inherently unoptimizable about the given lookup through a prototype chain, which is not true at all. Hence my initial comment trying to reveal what is hidden underneath.
Ultimately, I think, we should not mix together descriptions of how property lookup is <i>specified</i> and how it is <i>implemented</i>, because the latter depends on the concrete implementation and a multitude of factors. It can be implemented in a way that works much faster than a naive implementation that just follows the spec and makes every object a dictionary. Optimizing compiler can come and remove redundancies across lookup sites, hoist invariant guards etc.
It is very important to know how things are specified to behave, what JavaScript semantics really is, but it is actually as important to understand how implementations manage to implement this complicated semantics efficiently and make it fast. Especially if one want to write high performance but readable code... And when measuring things it is very important to see past the numbers, to understand what actually is happening under the hood.
[I have somewhat of a crusade against microbenchmarking, so please excuse me if I sound somewhat radical... People too often abuse/misuse it and then write code based on incorrect assumptions. I even once gave a talk where trying to explain the common pitfalls <a href="http://mrale.ph/talks/lxjs2013/">slides</a>, <a href="https://www.youtube.com/watch?v=65-RbBwZQdU">video</a>.]

diegocastorina

I completely agree about the fact that microbenchmarking is evil and that benchmarking is one of the most difficult tasks as the results depend on a multitude of factors.
Thanks a lot for your comments and for sharing this video :)

Esailija

Even if the focus of the post isn't performance, you shouldn't just deliver flat out incorrect information. There is nothing wrong with a micro benchmark as long as you understand why you got the results it gives before drawing conclusions.
It is possible to craft a convincing micro benchmark to support literally **any** theory because the untrained eye doesn't notice the subtle tricks used to create the illusion.
Examples:
Want to show that closures are as fast as prototypes or faster? One way would be to create a benchmark where the "class declaration" ends up getting re-evaluated - without anybody noticing this (very easy in jsperf). This is an illusion because this would be absurd to write in real code yet you are benchmarking it as if you would.
Want to show that doing tricks like <code>~~</code> to parse numbers is at least an order of magnitude faster than calling <code>parseInt</code>? Create a benchmark where you are parsing a hard coded string literal. Again this is an illusion because you are only showing that ~~ used with a constant string can be calculated at compile time while doing the same with parseInt cannot be - in real code you wouldn't run into this at all.
Want to show that long prototype chains are slower than short ones or closure variables? Create a benchmark that doesn't actually compare it to loading from short prototypes or closures, just compare it to a local variable - doesn't make any sense but somehow people will believe it.
Also,
<blockquote>
The lookup operations we’ve described, whether based on the prototype chain or the scope chain, are repeated every time a property or variable is accessed. When this lookup occurs within loops or other intensive operations, it can have significant performance ramifications, especially in light of the single-threaded nature of JavaScript which prevents multiple operations from happening concurrently.
</blockquote>
A lookup in a scope chain and or a prototype chain is a 100% sequential operation, it would take exactly the same time to do it even if you put a billion cores at it because there is nothing to parallelize.

Great article. Small issue-
It is usually not a good practice to invoke the Parent constructor when linking the Child's prototype
(i.e Child.prototype = new Parent()), since it may cause undesired side effects from invoking this constructor.
It is safer to set it as an empty object:
Child.prototype = Object.create(Parent.prototype)
And to invoke the Parent's constructor inside the Child's constructor with Parent.call(this).

liuxiaolei

just cross here, I've conducted a statistic calculation and test on the efficiency issues relating to javascript, see:
http://everdance.github.io/javascript/2014/04/03/data-access-efficiency-in-javascript.html

Jerome Covington

Not caching properties or jQuery selectors as a variable before entering into a loop is such a basic performance gotcha, but I still see it in a lot of code.

diegocastorina

I updated the code, thanks for the catch!

diegocastorina

Actually the two approaches are equivalent and the end result is the same. You will need to invoked Parent.call(this) inside Child's constructor anyway if you want the parent constructor to initialize Child's instances.

Liad Yosef

Well, the <code>Child.prototype = new Parent()</code> creates an extra full instance of Parent without any purpose.
If the Parent's constructor has side effects (like keeping track of the number of created instances, or performing a very heavy operation, for example) - they will happen at this point.
Plus, if the Parent's constructor requires dynamic arguments - they can't really be passed here.
Since all you really need here is to link the prototype - it is actually safer to take the <code>Child.prototype = Object.create(Parent.prototype)</code> approach - and invoke the Parent's constructor only when needed (inside the Child's constructor).
So regarding the structure and contents of the prototype chain, the end results are actually not the same.

thx a bunch for delivering that important "premature optimization is the root of all evil" quote. it's probably just as important as the similar "flat is better than nested" adage, which would point us to "avoid deeply nested inheritance trees and prototype chains". yet those nested architectures may at times be beneficial / desirable / virtually unavoidable—at other times, they're just CS grad ooze (as greatly demonstrated in the great (and realistic!) https://github.com/EnterpriseQualityCoding/FizzBuzzEnterpriseEdition scam; see https://github.com/loveencounterflow/coffeelibre#the-passive-aggressive-treatment-of-java-in-aoo for more links).
but however you architecture your code—if you want to keep performant, you'd typically (1) choose the practice that's well known to be better than all the wrong practices; (2) otherwise don't worry and strive for **code correctness**—which is all-important as no-one is interested in fast, but wrong results; (3) profile your app with real-world scenarios and try to identify & fix those most
salient bottlenecks.
see, doing `x.f()` *might* cause a terribly inefficient lookup in your app, but then maybe it only gets called a couple times, while another `x.g()` lookup, while being better in single-execution time, gets executed millions of times. you might still have to conclude that even that heavier load is not caused by *lookup time*, but lies in the *execution time* of `x.g()`, so you'd have to fix something inside of `x.g` rather than the way it's accessed from `x`.
i find it interesting how difficult JS prototypal inheritance and its ramifications are to people. it sure would appear to be simpler than, say, Python's 'classical' model, but then discussions always reveal subtle points of that model which *most* people tend to ignore or get wrong. there's jolly good reasons there are so many attempts out there to 'fix' prototypal inheritance. my best guess at this time is mixins.

diegocastorina

You are right! I think I had not completely understood myself how Object.create could be used on prototypes. Thanks a lot!

diegocastorina

You are right! I updated the example to use Object.create. Thanks!

Yoorek

Bad example. "Good way" works only because you put A.call(this) in B constructor not because you used Object.create(), below works the same with new():
function A () { this.x = 42; };
function B () { A.call(this); };
B.prototype = new A();
var b = new B();
b.hasOwnProperty('x'); // => true

tjwoods

The point is that when you define <code>B.prototype = new A();</code>, `B.prototype` becomes object with `x` too, but this key should not be in prototype object. Another example to confirm that using `new` oprator for prototype defining is `function A () { /* some heavy preparation code */ }`

tjwoods

<blockquote>It is worth noting that browsers also store a reference to the prototype of any object in a __proto__ property</blockquote>Strictly speaking, <code>__proto__</code> is a getter/setter of internal <code>[[Prototype]]</code> property of an object <b>defined</b> in Object.prototype (so it is not a property of an object itself) [<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto">mdn</a>].
What's why we can be confused why <code>o.__proto__</code> is <code>undefined</code> (but should be <code>null</code>) when we create object with <code>null</code> prototype (<code>var o = Object.create(null)</code>).
And btw, <code>__proto__</code> works everywhere and now is <a href="http://people.mozilla.org/~jorendorff/es6-draft.html#sec-object.prototype.__proto__">part of ES6</a>.

Originally from Italy, Diego spent several years working in The Netherlands before moving to Prague, in the Czech Republic. There he has been working as a senior freelance engineer in addition to working on his own company: a web-based CRM for small businesses.