Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
143 lines (104 sloc) 4.85 KB

JavaScript uses prototype-based inheritance, which is slightly different than inheritance in other languages. Suppose you create a new vanilla JavaScript object:

const obj = new Object();

obj instanceof Object; // true
obj.constructor === Object; // true

The instanceof operator tells you whether a given object is an instance of a given JavaScript class. You can also use instanceof with your own custom class:

class MyClass {}

const obj = new MyClass();

obj instanceof MyClass; // true
obj instanceof Object; // true

obj.constructor === MyClass; // true

The prototype Property

In JavaScript, every class has a prototype property. A class' prototype is an object. For example, MyClass.prototype is an object:

MyClass.prototype; // MyClass {}

JavaScript has a built-in Object.getPrototypeOf() function that lets you get the prototype of an object.

const obj = new MyClass();

Object.getPrototypeOf(obj) === MyClass.prototype; // true

In JavaScript, checking if an object obj is an instance of a given class C (excluding inheritance) is equivalent to checking if Obj.getPrototypeOf(obj) === C.prototype.

Setting Prototype Properties

Prototypes are just JavaScript objects. When you access a property on an object, like accessing obj.foo, JavaScript first looks to see if obj has a foo property using hasOwnProperty(). If it doesn't, JavaScript then checks the constructor's prototype for a foo property. For example:

const obj = new MyClass();

// If you set `foo` on `obj`'s constructor's prototype, that means you can
// access `obj.foo`.
MyClass.prototype.foo = 42;

obj.foo; // 42

// `foo` is an _inherited property_ as opposed to an _own property_.
obj.hasOwnProperty('foo');

The Prototype Chain

So instances of MyClass point up to MyClass.prototype, and checking whether an object is an instance of MyClass is defined as checking whether Object.getPrototypeOf() returns MyClass.prototype. Where does inheritance come in?

The trick is that, since MyClass.prototype is an object, Object.getPrototypeOf(MyClass) points to the prototype of the base class Object.

Object.getPrototypeOf(MyClass.prototype) === Object.protype; // true

If you use the extends keyword for inheritance, Object.getPrototypeOf() helps you get the base class's prototype:

class BaseClass {}
class SubClass extends BaseClass {}

Object.getPrototypeOf(SubClass.prototype) === BaseClass.prototype; // true
Object.getPrototypeOf(SubClass.prototype) === Object.prototype; // false

// Since `BaseClass` implicitly inherits from object:
Object.getPrototypeOf(BaseClass.prototype) === Object.prototype;

Surprisingly enough, a class's prototype has a constructor property that points to the class itself. So to get the BaseClass constructor from SubClass, you should get the prototype's constructor:

Object.getPrototypeOf(SubClass.prototype).constructor === BaseClass; // true

Given any class, you can walk up the inheritance tree and end up at Object.prototype. The prototype chain is the idea that you can follow a list of prototypes to get every class in an inheritance hierarchy.

class BaseClass {}
class SubClass extends BaseClass {}

let curPrototype = SubClass.prototype;

// Prints "SubClass", "BaseClass", "Object"
while (curPrototype != null) {
  console.log(curPrototype.constructor.name);
  curPrototype = Object.getPrototypeOf(curPrototype);
}

For example, you can implement your own instanceof operator by walking up the prototype chain yourself. Given v instanceof C, The instanceof operator walks up v's prototype chain to see if it can find C.prototype.

function isInstanceOf(obj, Class) {
  const prototypeToFind = Class.prototype;

  let curPrototype = obj.constructor.prototype;
  while (curPrototype != null) {
    if (curPrototype === prototypeToFind) {
      return true;
    }
    curPrototype = Object.getPrototypeOf(curPrototype);
  }
  return false;
}

Moving On

JavaScript prototypes can be confusing, but ES6 classes and Object.getPrototypeOf() have done a great job in making prototype-based inheritance more comprehensible. Iterating up the prototype chain is just a simple while() loop with Object.getPrototypeOf(), and you can easily access the actual class using .constructor.

You can’t perform that action at this time.