Cover image
Web Front-end
11 minute read

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

JavaScript is much more nuanced than most developers initially realize. Even for those with more experience, some of JavaScript’s most salient features continue to be misunderstood and lead to confusion. One such feature, described in this article, is the way that property and variable lookups are performed and the resulting performance ramifications to be aware of.

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:

function Person(firstName, lastName) {
  this.firstName = firstName;
  this.lastName = lastName;
}

var p1 = new Person('John', 'Doe');
var p2 = new Person('Robert', 'Doe');

In the above example, p1 and p2 are two different objects, each created using the Person function as a constructor. They are independent instances of Person, as demonstrated by this code snippet:

console.log(p1 instanceof Person); // prints 'true'
console.log(p2 instanceof Person); // prints 'true'
console.log(p1 === p2);            // prints 'false'

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.

The relationship between JavaScript prototype and the scope chain is important

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:

// Extending the Person prototype from our earlier example to
// also include a 'getFullName' method:
Person.prototype.getFullName = function() {
  return this.firstName + ' ' + this.lastName;
}

// Referencing the p1 object from our earlier example
console.log(p1.getFullName());            // prints 'John Doe'
// but p1 can’t directly access the 'prototype' object...
console.log(p1.prototype);                // prints 'undefined'
console.log(p1.prototype.getFullName());  // generates an error

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:

// We reference p1.getFullName, *NOT* p1.prototype.getFullName,
// since p1.prototype does not exist:

p1.getFullName = function(){
  return 'I am anonymous';
}

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:

console.log(p1.getFullName()); // prints 'I am anonymous'
console.log(p2.getFullName()); // prints 'Robert Doe'

See how P1 and P2 relate to the Person prototype in this JavaScript prototype example.

Another important thing to be aware of is that it’s also possible to dynamically change an object’s prototype. For example:

function Parent() {
  this.someVar = 'someValue';
};

// extend Parent’s prototype to define a 'sayHello' method
Parent.prototype.sayHello = function(){
    console.log('Hello');
};

function Child(){
  // this makes sure that the parent's constructor is called and that
  // any state is initialized correctly. 
  Parent.call(this);
};

// extend Child's prototype to define an 'otherVar' property...
Child.prototype.otherVar = 'otherValue';

// ... but then set the Child's prototype to the Parent prototype
// (whose prototype doesn’t have any 'otherVar' property defined,
//  so the Child prototype no longer has ‘otherVar’ defined!)
Child.prototype = Object.create(Parent.prototype);

var child = new Child();
child.sayHello();            // prints 'Hello'
console.log(child.someVar);  // prints 'someValue'
console.log(child.otherVar); // prints 'undefined' 

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

This diagram shows an example of the relationship between JavaScript prototypes in a prototype chain.

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.

Consider the following code:

// global context
var message = 'Hello World';

var sayHello = function(n){
  // local context 1 created and pushed onto context stack
  var i = 0;
  var innerSayHello = function() {
    // local context 2 created and pushed onto context stack
    console.log((i + 1) + ':  ' + message);
    // local context 2 popped off of context stack
  }
  for (i = 0; i < n; i++) {
    innerSayHello();
  }
  // local context 1 popped off of context stack
};

sayHello(3);
// Prints:
// 1:  Hello World
// 2:  Hello World
// 3:  Hello World

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:

The way the scope chain relates to the objects is outlined in this JavaScript 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:

function Person(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
};

function persist(person) {
  with (person) {
    // The 'person' object was pushed onto the scope chain when we
    // entered this "with" block, so we can simply reference
    // 'firstName' and 'lastName', rather than person.firstName and
    // person.lastName
    if (!firstName) {
      throw new Error('FirstName is mandatory');
    }
    if (!lastName) {
      throw new Error('LastName is mandatory');
    }
  }
  try {
    person.save();
  } catch(error) {
    // A new scope containing the 'error' object is accessible here
    console.log('Impossible to store ' + person + ', Reason: ' + error);
  }
}

var p1 = new Person('John', 'Doe');
persist(p1);

To fully understand how scope-based variable lookups occur, it is important to keep in mind that in JavaScript there are currently no block-level scopes. For example:

for (var i = 0; i < 10; i++) {
  /* ... */
}
// 'i' is still in scope!
console.log(i);  // prints '10'

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.

Consider the following example:

var start = new Date().getTime();
function Parent() { this.delta = 10; };

function ChildA(){};
ChildA.prototype = new Parent();
function ChildB(){}
ChildB.prototype = new ChildA();
function ChildC(){}
ChildC.prototype = new ChildB();
function ChildD(){};
ChildD.prototype = new ChildC();
function ChildE(){};
ChildE.prototype = new ChildD();

function nestedFn() {
  var child = new ChildE();
  var counter = 0;
  for(var i = 0; i < 1000; i++) {
    for(var j = 0; j < 1000; j++) {
      for(var k = 0; k < 1000; k++) {
        counter += child.delta;
      }
    }
  }
  console.log('Final result: ' + counter);
}

nestedFn();
var end = new Date().getTime();
var diff = end - start;
console.log('Total time: ' + diff + ' milliseconds');

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:

function nestedFn() {
  var child = new ChildE();
  var counter = 0;
  var delta = child.delta;  // cache child.delta value in current scope
  for(var i = 0; i < 1000; i++) {
    for(var j = 0; j < 1000; j++) {
      for(var k = 0; k < 1000; k++) {
        counter += delta;  // no inheritance tree traversal needed!
      }
    }
  }
  console.log('Final result: ' + counter);
}

nestedFn();
var end = new Date().getTime();
var diff = end - start;
console.log('Total time: ' + diff + ' milliseconds');

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.

We’ll start by running the first example in a node.js REPL:

diego@alkadia:~$ node test.js 
Final result: 10000000000
Total time: 8270 milliseconds

So that takes about 8 seconds to run. That’s a long time.

Now let’s see what happens when we run the optimized version:

diego@alkadia:~$ node test2.js 
Final result: 10000000000
Total time: 1143 milliseconds

This time it took just one second. Much faster!

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.

Further Reading on the Toptal Engineering Blog:

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/
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?
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?
Cesar William
Good article! Just a correction: change: function Parent( this.someVar = 'someValue'; ){} to: function Parent(){ this.someVar = 'someValue'; }
Cesar William
Good article! Just a correction: change: function Parent( this.someVar = 'someValue'; ){} to: function Parent(){ this.someVar = 'someValue'; }
Denny Ferrassoli
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/
Denny Ferrassoli
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. ]
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.
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 just one VM, than you should pick the slowest and not the fastest one.
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 just one VM, than you should pick the slowest and not the fastest one.
H. Singer
Great catch. Thanks. Just corrected this.
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 be always to write the best code possible. Optimization should be done only after performance issues arise. Remember: "premature optimization is the root of all evil" :)
diegocastorina
It all depends on the VM implementation and you cannot make any assumption on it. However, your main focus has to be always to write 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 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 how 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 (e.g. that V8 does not hit the same issue for methods, which is the most common thing to put on prototypes, how newer V8 fixes the issue, etc), 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 possess sophisticated optimization pipelines.
Vyacheslav Egorov
That page in the V8 documentation was written quite a while ago and as I said already these days most engines 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 how 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 (e.g. that V8 does not hit the same issue for methods, which is the most common thing to put on prototypes, how newer V8 fixes the issue, etc), 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 possess 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.
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>, maybe it conveys the reasons driving me better then my comments :)]
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>, maybe it conveys the reasons driving me better then my comments :)]
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 :)
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.
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.
Fazle Elahee
Excellent article.
Fazle Elahee
Excellent article.
Filipe Constantinov Menezes
I would also suggest a modification of the prototype chain image. function Entity -> Function -> Object new Entity() -> Entity -> Object Consider the following snipped: //ES 5.1 compilant function TempConstructor() { if(!(this instanceof TempConstructor)) throw new Error('Must be called with new'); } TempConstructor.prototype.foo = function () { return 'foo'; }; function displayInheritance(o, str) { var r = str || ''; var p = Object.getPrototypeOf(o); while (p !== null) { r += ' -> ' + p.constructor.name; p = Object.getPrototypeOf(p); } console.log(r); } displayInheritance(Object.create(null), 'Object.create(null)'); displayInheritance(new Object(), 'new Object()'); displayInheritance({"key": "value"}, '{"key": "value"}'); displayInheritance(["Array", "Of", "Items"], '["Array", "Of", "Items"]'); displayInheritance(new TempConstructor(), 'new TempConstructor()'); displayInheritance(TempConstructor, 'TempConstructor'); *NOTE*: Modified to simpler code
Filipe Constantinov Menezes
I would also suggest a modification of the prototype chain image. function Entity -> Function -> Object new Entity() -> Entity -> Object Consider the following snipped: //ES 5.1 compilant function TempConstructor() { if(!(this instanceof TempConstructor)) throw new Error('Must be called with new'); } TempConstructor.prototype.foo = function () { return 'foo'; }; function displayInheritance(o, str) { var r = str || ''; var p = Object.getPrototypeOf(o); while (p !== null) { r += ' -> ' + p.constructor.name; p = Object.getPrototypeOf(p); } console.log(r); } displayInheritance(Object.create(null), 'Object.create(null)'); displayInheritance(new Object(), 'new Object()'); displayInheritance({"key": "value"}, '{"key": "value"}'); displayInheritance(["Array", "Of", "Items"], '["Array", "Of", "Items"]'); displayInheritance(new TempConstructor(), 'new TempConstructor()'); displayInheritance(TempConstructor, 'TempConstructor'); *NOTE*: Modified to simpler code
Liad Yosef
Great article. Small issue - It is usually not a good practice to invoke the Parent constructor when linking the Child's prototype (i.e <code>Child.prototype = new Parent()</code>), since it may cause undesired side effects from invoking this constructor. It is safer to set it as an empty object: <pre><code class="javascript"> Child.prototype = Object.create(Parent.prototype) </code></pre> And to invoke the Parent's constructor inside the Child's constructor with <pre><code class="javascript">Parent.call(this)</code></pre>.
Liad Yosef
Great article. Small issue - It is usually not a good practice to invoke the Parent constructor when linking the Child's prototype (i.e <code>Child.prototype = new Parent()</code>), since it may cause undesired side effects from invoking this constructor. It is safer to set it as an empty object: <pre><code class="javascript"> Child.prototype = Object.create(Parent.prototype) </code></pre> And to invoke the Parent's constructor inside the Child's constructor with <pre><code class="javascript">Parent.call(this)</code></pre>.
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
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.
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
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.
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.
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.
tjwoods
<blockquote>Actually the two approaches are equivalent and the end result is the same.</blockquote> This is wrong statement. Using <code>new</code> operator to define prototype considered as a bad practice. <pre> function A () { this.x = 42; }; function B () {}; B.prototype = new A(); var b = new B(); b.x; // => 42 // that's bad: b.hasOwnProperty('x'); // => false b.__proto__.hasOwnProperty('x'); // => true // Good way function B () { A.call(this); }; B.prototype = Object.create(A.prototype); var b = new B(); b.hasOwnProperty('x'); // => true </pre>
tjwoods
<blockquote>Actually the two approaches are equivalent and the end result is the same.</blockquote> This is wrong statement. Using <code>new</code> operator to define prototype considered as a bad practice. <pre> function A () { this.x = 42; }; function B () {}; B.prototype = new A(); var b = new B(); b.x; // => 42 // that's bad: b.hasOwnProperty('x'); // => false b.__proto__.hasOwnProperty('x'); // => true // Good way function B () { A.call(this); }; B.prototype = Object.create(A.prototype); var b = new B(); b.hasOwnProperty('x'); // => true </pre>
~flow
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.
~flow
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 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!
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
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>, <code>B.prototype</code> becomes new object with key `x` too, but this key should not be in prototype object. Another example to confirm that using `new` oprator for prototype defining is <code>function A () { /* some heavy preparation code mutating `this` */ }</code>
tjwoods
The point is that when you define <code>B.prototype = new A();</code>, <code>B.prototype</code> becomes new object with key `x` too, but this key should not be in prototype object. Another example to confirm that using `new` oprator for prototype defining is <code>function A () { /* some heavy preparation code mutating `this` */ }</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>.
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>.
RAHUL GARG
Kindly explain difference between below approaches......When we define function within object or when with prototype ? 1. function Parent(){ var self = this; self.commonValue = 'CommonValue'; self.commonFunction = function(){ return 123; }; }; function Child(){ Parent.call(this); }; var c = new Child(); c.commonFunction(); 2. function Parent(){ var self = this; self.commonValue = 'CommonValue'; }; Parent.prototype.commonFunction = function(){ return 123; }; function Child(){ }; Child.prototype = new Parent(); var c = new Child(); c.commonFunction();
RAHUL GARG
Kindly explain difference between below approaches......When we define function within object or when with prototype ? 1. function Parent(){ var self = this; self.commonValue = 'CommonValue'; self.commonFunction = function(){ return 123; }; }; function Child(){ Parent.call(this); }; var c = new Child(); c.commonFunction(); 2. function Parent(){ var self = this; self.commonValue = 'CommonValue'; }; Parent.prototype.commonFunction = function(){ return 123; }; function Child(){ }; Child.prototype = new Parent(); var c = new Child(); c.commonFunction();
Yuan
Good article. I think Entity should have a Function.prototype, isn't it?
Yuan
Good article. I think Entity should have a Function.prototype, isn't it?
Heena Kwag
Hello Diego Castorina, thank you for the helpful article! I would like to ask you for permission to translate this article into Korean and publish it on a company run blog (https://ui.toast.com/) and on our Github wiki page (https://github.com/nhn/fe.javascript). I will quote the original source and in no way use it to gain any monetary value. Please let me know what you think, and hope you have a great day :)
Heena Kwag
Hello Diego Castorina, thank you for the helpful article! I would like to ask you for permission to translate this article into Korean and publish it on a company run blog (https://ui.toast.com/) and on our Github wiki page (https://github.com/nhn/fe.javascript). I will quote the original source and in no way use it to gain any monetary value. Please let me know what you think, and hope you have a great day :)
Heena Kwag
Hello Diego Castorina, thank you for the helpful article! I would like to ask you for permission to translate this article into Korean and publish it on a company run blog (https://ui.toast.com/) and on our Github wiki page (https://github.com/nhn/fe.javascript). I will quote the original source and in no way use it to gain any monetary value. Please let me know what you think, and hope you have a great day :)
comments powered by Disqus