JavaScript Trivia I: Prototypal Inheritance
All JavaScript functions are also objects, and every object upon creation is assigned a prototype property, which is initialized to an empty object. We can define properties on this prototype object, such as the describe property on the Animal object below:
function Animal(name, color) { this.name = name; this.color = color; } Animal.prototype.describe = function() { console.log(`${this.name} is a ${this.color} animal!`); }
Sidenote: The above is equivalent to class Animal {... and a constructor function -- class syntax in JS is just syntactic sugar for a constructor function (just a capitalized function, by convention) and an associated prototype object.
Imagine you call a method on an object instance, like calling the method describe on the whale instance of the Animal object:
const whale = new Animal('Wilson', 'blue'); whale.describe(); // Wilson is a blue whale!
When we create whale as an instance of Animal, we are linking whale to Animal by a property whale.__proto__, which by definition points to the prototype object of Animal. I.e.:
whale.__proto__ === Animal.prototype // true
Now, when we call whale.describe(), the JS engine will first look in whale’s prototype (which is Animal's prototype) for the method. It finds it there, so it uses it. Awesome.
If it didn't find describe in that first prototype, it would look "up the prototype chain", i.e. at the object’s parent (Function), then the parent’s parent (Object). If it gets all the way to the base Object (from which all objects inherit) without finding the method in question, it assigns it the value undefined, potentially throwing an error depending on how you're referencing it.
The prototype chain also comes into play when we make one class inherit from another. Let's see what happens when we make a Cat class that inherits from Animal.
We want Cat to access everything Animal has, like name and color, but also to have the cat-specific property breed.
We can do this with class syntax:
class Cat extends Animal { constructor(name, color, breed) { super(name, color); this.breed = breed; } }
This creates a new Cat class with its own (initially empty) prototype object, and by including extends Animal we point its __proto__ property to Animal.prototype:
> Cat.__proto__ [Function: Animal]
You can technically use the old school syntax to set up this child-parent relationship as well:
function Cat(name, color, breed) { Animal.call(this, name, color); this.breed = breed; } Cat.__proto__ = Object.create(Animal.prototype);
Without the syntactic sugar of class and extend, we have to explicitly add Animal to Cat's prototype chain by pointing its __proto__ property to Animal's prototype (otherwise it would have pointed to Function.prototype by default).
This is closer to what we learned at App Academy but is deprecated according to MDN (link), so unless you are an expert I recommend you stick with the sugary version.














