Notes On JavaScript Prototypal Inheritance
I’ve recently been working a lot with JavaScript (specifically, Backbone with Marionette) after many years of working predominantly with Java. When I start something that is a little different for me, it is always my tendency to want to jump in and start using it immediately. But in this situation, I have found that understanding the basics in parallel is almost always beneficial. It prevents a lot of misunderstandings and saves a lot of time.
Think of it like researching some basics of the language of a destination before you board a plane versus telling yourself you will look at a book during the plane ride. Realistically, you will be much more interested in wine and a nap on the plane – it’s probably too late at that point anyway, and there is a really good chance you will be glad you planned ahead at some point in your near future.
In any event, if you sit down to write some serious JavaScript, even if you are using a framework (or perhaps especially if you are using a framework), you might possibly find a need to understand a bit about JavaScript inheritance. Understanding basic concepts can be helpful even if you may not be using them directly. If you are a long-time JS person, good news: you can skip right to the wine. But if you are new to JavaScript prototypal inheritance, here are some notes to help you along your way. This is obviously not comprehensive, but intended to give an overview of items to keep in mind.
JavaScript Prototypal Inheritance
If you are a Java developer, you are used to classical inheritance and thinking in terms of classes. Your inherited objects get their definition from preexisting classes. At first glance, you may look at JavaScript and see a new object being created and think the things happening look fairly similar.
However, JavaScript uses prototypal inheritance, which involves cloning a parent object. The objects are templates for creating the next object. So, in JavaScript, there are no classes and an object inherits properties from another object. And an object is just unordered key/value pairs that happens to act as a definition of an object. By the way, anything that isn’t a primitive in JS is an object.
Concepts to keep in mind:
1. One thing to consider in JS prototypal inheritance, is that properties are shared and that this sharing may be unexpected to those who are used to classical inheritance. So, if you have a parent with an array property, you may be tempted to think that if you have two classes inheriting from that parent that they both each get an array to themselves. This isn’t the case, though. They share that array.
So, if you add something to that array from a second (or third, fourth, or twentieth) inherited object, then the first one (and all of the others) will now also have what you added to that property that is being referencing. Ultimately, this is a big part of how the prototype chain works, and it’s not a big deal, but don’t let yourself be surprised by this.
There are some common ways around this when you need different behavior including putting intended instance properties in a constructor or using an initialize function. Each of these has pros and cons such as the need to remember to call subclass.prototype.initialize
in all subclasses if you go the initialize route.
2. Inheritance in JavaScript works by full searching up the prototype tree until the property is found or no property is found, eventually returning ‘undefined’ if not found. This is the essence of the inheritance (or your properties and inherited properties if you prefer to look at it that way). So the lookup goes up the prototype hierarchy while ‘this’ still points to the current inheriting element.
This is true for reads, but writing to the object – either assigning a value to a property or deleting a property – does not go up the chain, but stays at the current object referenced by this. In long chains, going up the tree is something to keep in mind from a memory usage perspective. Also, keep in mind that you can check to see if a property belongs to the current object using the hasOwnProperty()
or the in operator method.
And one more thing – the last stop is actually always Object.prototype
from which all reference types inherit by default.
3. Keeping in mind the above two concepts, instances always get the latest inherited values no matter when they were changed. And, since methods are just another property, and since they only appear to by properties of an object but actually belong to the parent, you can add new ones at any time and dynamically change behavior.
4. Interface inheritance doesn’t exist in JavaScript. If you think about this in terms of the above this makes sense, as it is all about the unordered key/value pairs and clones here, and JavaScript functions don’t have signatures.
5. Since everything is a chain lookup, properties are shadowed, not overridden, and access to the shadowed property on the parent prototype on the object is blocked. The first one found on the aforementioned chain lookup is the one used even if other properties of that name exist higher in the chain.
6. Overloading doesn’t technically exist. Instead of creating several methods with different signatures, you can have a master function. Since JS doesn’t type check arguments or require a certain number of arguments, you can work accordingly inside the method – processing from there or delegating to different methods.
Alright, hopefully this is a helpful start. It’s time to get excited about your flight.
Reference: | Notes On JavaScript Prototypal Inheritance from our WCG partner Keyhole Software at the Keyhole Software blog. |
You said “JavaScript uses prototypal inheritance, which involves cloning a parent object”
Actually, it doesn’t clone the object. It just points to it via the prototype chain. That is the key to how prototypal inheritance works and is the mechanism that causes changes to the ‘parent’ object being visible in the ‘child’ object.