Prototypal Inheritance in JavaScript
Prototypal: JavaScript is unique in a lot of ways. It’s the major browser-supported language that powers most user interfaces online. It has its own quirks in syntax and equality that can range from endearing to maddeningly annoying. However, JavaScript’s biggest unique point is object inheritance and the prototype chain.
In this outline, we will cover the essentials on prototypal inheritance in JavaScript.
Quick Recap: What Is Inheritance?
In most object-oriented programming languages, there is a mechanism for child objects to inherit methods and attributes from their parents. Popular languages like Java, C#, C++, and Python (among many others) all support this type of inheritance. Defining these objects and their inheritance structures is a matter of creating classes that extend one another. A class is a definition of attributes and behaviors, an object is an instance of a given class.
For instance, you might have a base Animal class with methods that allow animals to eat() and sleep(). Then, your Dog class inherits the eat() and sleep() methods from its Animal parent class while also defining its own methods like bark() and be_undyingly_loyal().
Inheritance is one of the core principles of object-oriented design. It helps build reusable and modular components of software applications because we can inherit from and extend the parent class in many different ways.
Inheritance
in JavaScript
However, JavaScript is not a class-based language. While there is a class keyword in JavaScript since 2015, it’s only syntactic sugar. Under the hood, JavaScript has its own mechanism for defining and resolving inheritance relationships.
This mechanism is known as the prototype chain. There are two key things to know about the JavaScript prototype chain and how it differs from class-based languages. If you take nothing else from this article, these two points will get you pretty far in understanding JavaScript inheritance.
1. Prototypes are objects themselves
In class-based languages, the classes act as blueprints. No object of that class actually exists until you instantiate it in your program. So, you define the class, then you create an instance of that class.
In JavaScript, prototypes are objects themselves. In fact, every function in JavaScript has a __proto__ attribute and a special property called `prototype` that allows you to access the __proto__ attributes.
If you open your console in your browser (ctrl+shift+I in Chrome & Firefox), you can see this for yourself.
Let’s create a new function that does absolutely nothing. Just leave it empty:
ZnVuY3Rpb24gZG9Tb21ldGhpbmcoKXt9
Then, let’s check out the default prototype property that JavaScript created for us:
Y29uc29sZS5sb2coIGRvU29tZXRoaW5nLnByb3RvdHlwZSApOw==
ewogICAgY29uc3RydWN0b3I6IMaSIGRvU29tZXRoaW5nKCksCiAgICBfX3Byb3RvX186IHsKICAgICAgICBjb25zdHJ1Y3RvcjogxpIgT2JqZWN0KCksCiAgICAgICAgaGFzT3duUHJvcGVydHk6IMaSIGhhc093blByb3BlcnR5KCksCiAgICAgICAgaXNQcm90b3R5cGVPZjogxpIgaXNQcm90b3R5cGVPZigpLAogICAgICAgIHByb3BlcnR5SXNFbnVtZXJhYmxlOiDGkiBwcm9wZXJ0eUlzRW51bWVyYWJsZSgpLAogICAgICAgIHRvTG9jYWxlU3RyaW5nOiDGkiB0b0xvY2FsZVN0cmluZygpLAogICAgICAgIHRvU3RyaW5nOiDGkiB0b1N0cmluZygpLAogICAgICAgIHZhbHVlT2Y6IMaSIHZhbHVlT2YoKQogICAgfQp9
We can add data and methods to our doSomething prototype by setting them using .prototype:
ZG9Tb21ldGhpbmcucHJvdG90eXBlLmZvbyA9ICJiYXIiOw==
Then, if we look at the results of console.log( doSomething.prototype ); again we can see that new value:
ewogICAgZm9vOiAiYmFyIiwKICAgIGNvbnN0cnVjdG9yOiDGkiBkb1NvbWV0aGluZygpLAogICAgX19wcm90b19fOiB7CiAgICAgICAgY29uc3RydWN0b3I6IMaSIE9iamVjdCgpLAogICAgICAgIC4uLiAvLyBvdGhlciBkZWZhdWx0IG1ldGhvZHMgaGVyZQogICAgfQp9
2. JavaScript prototypal inheritance relationships are created/resolved on-the-fly at the time of interpretation
JavaScript is designed to run inside browsers, so it isn’t compiled and it needs to be simple to interpret and resolve at runtime.
The prototype chain keeps things simple by making everything a first-class object. Instead of class blueprints that can inherit once they’re instantiated as real objects, JavaScript just chains objects together to form inheritance relationships.
That might be a confusing sentence to read, and sometimes JavaScript’s chaining rules are confusing, so let’s look at an example:
bGV0IEFuaW1hbCA9IGZ1bmN0aW9uIChuYW1lKSB7IC8vIGNyZWF0ZSBhbiBBbmltYWwgYmFzZSBwcm90b3R5cGUKICAgIHRoaXMubmFtZSA9IG5hbWUgLy8gc2V0IEFuaW1hbCBhdHRyaWJ1dGVzIHVzaW5nIHRoZSDigJx0aGlz4oCdIGtleXdvcmQgb24gQW5pbWFsCn0KQW5pbWFsLnByb3RvdHlwZS5lYXQgPSBmdW5jdGlvbiAoKSB7IC8vIGFkZCBBbmltYWwgcHJvdG90eXBlIG1ldGhvZHMKICAgIGNvbnNvbGUubG9nKGAke3RoaXMubmFtZX0gaXMgZWF0aW5nYCkgCn0KbGV0IERvZyA9IGZ1bmN0aW9uIChuYW1lLCBicmVlZCkgeyAvLyBjcmVhdGUgYSBEb2cgc3ViLXByb3RvdHlwZQogICAgQW5pbWFsLmNhbGwodGhpcywgbmFtZSkgLy8gY2FsbCB0aGUgQW5pbWFsIG9iamVjdCBhcyBhIGNvbnN0cnVjdG9yCiAgICB0aGlzLmJyZWVkID0gYnJlZWQgLy8gc2V0IERvZyBhdHRyaWJ1dGVzIHVzaW5nIHRoZSDigJx0aGlz4oCdIGtleXdvcmQgb24gRG9nCn0KRG9nLnByb3RvdHlwZSA9IE9iamVjdC5jcmVhdGUoQW5pbWFsLnByb3RvdHlwZSkgLy8gZ2l2ZSBEb2cgYWNjZXNzIHRvIEFuaW1hbCBtZXRob2RzCkRvZy5wcm90b3R5cGUuYmFyayA9IGZ1bmN0aW9uICgpIHsgY29uc29sZS5sb2co4oCYV29vZiB3b29mIeKAmSkgfSAvLyBhZGQgRG9nLXNwZWNpZmljIG1ldGhvZHMKRG9nLnByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IERvZyAvLyByZXNldCBjb25zdHJ1Y3RvciBmb3IgbmV3IGRvZ3MgdG8gYmUgRG9nIGluc3RlYWQgb2YgQW5pbWFsCmZpZG8gPSBuZXcgRG9nKOKAmEZpZG/igJksIOKAmFBvaW50ZXLigJkpIC8vIHRoZSDigJxuZXfigJ0ga2V5d29yZCBqdXN0IGNyZWF0ZXMgYSBjb3B5IG9mIHRoZSBEb2cgZnVuY3Rpb24gJiBjYWxscyBpdApmaWRvLm5hbWUgLy8g4oCcRmlkb+KAnSBmcm9tIEFuaW1hbCA+PiB0aGlzLm5hbWUKZmlkby5icmVlZCAvLyDigJxQb2ludGVy4oCdIGZyb20gRG9nID4+IHRoaXMuYnJlZWQKZmlkby5lYXQoKSAvLyDigJxGaWRvIGlzIGVhdGluZ+KAnSBmcm9tIEFuaW1hbC5wcm90b3R5cGUuZWF0CmZpZG8uYmFyaygpIC8vIOKAnFdvb2Ygd29vZiHigJ0gZnJvbSBEb2cucHJvdG90eXBlLmJhcms=
If we look at fido’s prototype inheritance chain we can see these relationships:
fido.__proto__
- Animal {bark: ƒ, constructor: ƒ}
- bark: ƒ () <– Dog’s bark method
- constructor: ƒ (name, breed) <– Dog’s arguments for setting breed attribute
- arguments: null
- caller: null
- length: 2
- name: “Dog”
- prototype: Animal {bark: ƒ, constructor: ƒ}
- __proto__: ƒ ()
- [[Scopes]]: Scopes[2]
- __proto__:
- eat: ƒ () <– Animal’s eat method
- constructor: ƒ (name) <– Animal argument for setting name attribute
- __proto__: Object
When JavaScript finds that fido itself doesn’t have an eat method, it checks fido’s parent, Dog.prototype, methods. When Dog.prototype also doesn’t have an eat method, it checks Dog’s parent, Animal.prototype, where it finds the eat method.
Using the class
Keyword
When you use the class keyword, what’s really happening is what we see above. However, the class keyword makes it much easier to write. Now you can say:
Y2xhc3MgQW5pbWFsIHsKICAgIGNvbnN0cnVjdG9yKG5hbWUpIHsKICAgICAgICB0aGlzLm5hbWUgPSBuYW1lCiAgICB9CiAgICBlYXQoKSB7CiAgICAgICAgY29uc29sZS5sb2coYCR7dGhpcy5uYW1lfSBpcyBlYXRpbmdgKQogICAgfQp9CiAKY2xhc3MgRG9nIGV4dGVuZHMgQW5pbWFsIHsKICAgIGNvbnN0cnVjdG9yKG5hbWUsIGJyZWVkKSB7CiAgICAgICAgc3VwZXIobmFtZSkgLy8gaW5pdGlhbGl6ZSB0aGUgcGFyZW50IGNsYXNzCiAgICAgICAgdGhpcy5icmVlZCA9IGJyZWVkCiAgICB9CiAgICBiYXJrKCkgewogICAgICAgIGNvbnNvbGUubG9nKOKAmFdvb2Ygd29vZuKAmSkKICAgIH0KfQ==
Conclusion
Prototypal Inheritance in JavaScript
For JavaScript developers, it’s critical to know what your language is doing behind the scenes when you’re inheriting attributes and methods from one object to another. For developers who are familiar with other languages and new to JavaScript, understanding the prototype chain is going to be essential to getting stuff done.
The Fastest Way To Build Software Is “Right” The First Time!
Understanding your industry is one thing. Understanding the technology you are using is another. When you read studies that tell you that 75% of projects are doomed from the beginning, it has to make you pause before signing your name to the outcome.
Consider letting our proven professionals take a look at your project. They’ve seen what can go wrong and know how to avoid costly errors.
We build custom software from start to finish. We plug into your environment with the proven expertise you need for us to work independently or in co-development. And, we bring the soft-skills that make the task enjoyable, and the experience to leave your team stronger and ready to take over.
We Bring You…
Team-Complete™ Development
Soft-Skills For A Winning Experience
Sometimes the most critical person in the room is the one with a calm voice and the knowledge to select the right words. Bringing a development team together or presenting a clear concept for stakeholders can make all the difference between success or failure. Black Slate consultants are at the top of their field. They navigate challenging decisions, guide with a confident voice, and know when to get out of the way.