Object Oriented Java Script And Prototypal Inheritance

Conceptual Aside: Classical vs. Prototypal Inheritance

Inheritance where one object gets access to the properties and methods of another object.

Classical inheritance is the way that most programming languages handle inheritance. It's very robust but can get extremely complicated and intertwined.

Prototypal inheritance is used by JavaScript, and it's often much easier to understand.

Understanding the Prototype

All objects in JavaScript have a prototype property. The prototype property is a reference to another object.

Suppose I have an object with a property:

const obj = {
  prop1: "Hello!"
};

obj.prop1; // "Hello"
obj.prop2; // What does this do?

When you reference an object property, there's a search order:

  1. The JavaScript engine first looks in the object itself.

  2. If it can't find the property, it then looks inside its prototype for the property name.

  3. If it can't find it there, it looks inside the prototype's prototype, etc., etc.

This is known as the prototype chain.

Note: Notice that you don't have to manually reference the prototype. The JavaScript engine will automatically search down the prototype chain.

Accessing the prototype

You can directly set and access an object's prototype like this:

const person = {
  name: "Default",
  logName: function() { console.log(this.name) }
};

const dan = {
  name: "Dan"
};

// THIS TECHNIQUE IS NOT PERFORMANT! DON'T EVER USE IT!
dan.__proto__ = person;

// Now this works...
dan.logName(); // logs "Dan"

Note: this correctly references dan, even though it's coming from the prototype person.

Note 2: Notice that name in dan overrides name in person. That's because the prototype chain starts at the original object and stops searching when it finds the property.

Everything Is An Object (or A Primitive)

Everything in JavaScript reduces down to the base object prototype.

cons obj = {};
obj.__proto__; // Base object

const fn = function() { };
fn.__proto__; // Empty object containing apply, bind, call, etc.
fn.__proto__.__proto__; // Base object

const arr = [];
arr.__proto__; // Array object containing map, filter, length, etc.
arr__proto__.__proto__; // Base object

Reflection and Extend

Reflection is an object's ability to look at and change its own properties and methods.

const obj = {
  prop1: 1,
  prop2: 2
};

// We can access the properties of an object!
for (let prop in obj) {
  if (obj.hasOwnProperty(prop)) console.log(obj[prop]);
};

Extend is a useful code pattern that is an alternative to the prototype chain for combining and composing objects. Instead of inheriting properties and methods through the prototype chain, you can directly add new properties and methods through reflection.

// From underscore.js
_.extend(obj, { prop3: 3 });

The above method takes obj and adds properties from all objects after it to obj. That means obj will now have prop3.

Last updated