Object Oriented Programming (OOP) is a programming technique which uses objects to model real world things. Objects can contain data and code, as well as functionality or behavior. Data (and often functions) inside an object are said to be encapsulated and can be given a specific namespace.

A class or constructor is a template/blueprint that defines what characteristics an object should have. From a class, we can create object instances — objects that contain the data and functionality defined in the class.

Classes can be created based on other classes in a parent and child type of hierarchy called inheritance. This means you can reuse common functionality common to all object types without having to duplicate it, but you can also override or add specialized features as needed.

Note: Polymorphism is the fancy word for the ability of multiple object types to implement the same functionality.

Prototypes

In JavaScript objects are also functions, which means they have properties. Every object has a property called prototype which is itself an object.

function foo() {...}

typeof foo.prototype; // 'object'

A Constructor is a function meant to be used to construct new objects. In other words it’s a template or blueprint for what we want future objects to be. In JavaScript, there’s no real difference between a “regular” function and a constructor function, but by convention functions that are meant to be used as constructors are Capitalized.

Prototypal Inheritance

In a “classic” OO language all data is copied over into a new instance from a constructor function. This creates a parent/child relationship. But JavaScript is different.

In JavaScript, when a new object is created from a constructor function (which should be Capitalized), its core functionality is defined by its prototype which creates a reference chain called the prototype chain.

In the example below, fido doesn’t actually have its own method bark().

function Dog() { ... }

Dog.prototype.bark = function() {
  console.log('woof!');
};

const fido = new Dog();

fido.bark(); // 'woof!'
fido.hasOwnProperty('bark'); // false

What actually happens when we write fido.bark() is:

  1. The JS engine looks for a property called bark on the fido object
  2. It doesn’t find one, so it looks “up the prototype chain” to fido’s parent, which is Dog.prototype.
  3. It finds Dog.prototype.bark and calls it with this bound to fido.

To repeat since this is really important:

There is no such property fido.bark. It doesn’t exist. Instead, fido has access to the bark() method on Dog.prototype because it’s an instance of Dog. This is “invisible link” is commonly called the “prototype chain”.

Create a new object

The this keyword refers to the new object that you’re creating.

function Rectangle( width, height ) {
 this.width = width;
 this.height = height;
}

const rect = new Rectangle(3, 4);

rect.width; // 3
rect.height; // 4

The new keyword does two things here:

  1. Create an empty object that has Rectangle.prototype in its prototype chain
  2. Call Rectangle with the this bound to the new object.

To add a method called area to our Rectangle constructor:

Rectangle.prototype.area = function() {
  return this.width * this.height;
}

Note that this inside of a method refers to the instance. So…

const rect = new Rectangle(4, 5);
rect.area(); // 20

Subclassing

How do we make a new class of objects that inherits from Rectangle? Let’s start by creating its constructor.

function Square(length) {
  this.width = this.height = length;
}

To make Square inherit from Rectangle we need to setup the prototype chain. We can use Object.create() to create an empty object that inherits from another object.

Square.prototype = Object.create(Rectangle.prototype);

All instances of Square now automatically have Square.prototype in their prototype chain. And because Square.prototype has Rectangle.prototype in its prototype chain, every Square will have access to the methods of Rectangle.

const square = new Square(4);

square.area(); // 16

Time Travel

A really interesting and also dangerous thing about inheritance in JavaScript is that you can modify or extend the capabilities of a class after you’ve defined it!

JavaScript will look up the prototype when trying to access properties on an object, which means you can alter your classes at runtime.

For example (don’t ever do this):

const arr = [1,2,3,4,5];

Array.prototype.shuffle = function() {
  return this.sort(function() {
    return Math.round(Math.random() * 2) - 1;
  });
};

arr.shuffle(); // [3,1,4,5,2]

The important thing to note here is that arr was created before Array.prototype.shuffle existed. But because property lookups go up the prototype chain, our array got access to the new method anyway–because it existed on Array.prototype by the time we tried to actually use it. It’s like we went back in time and gave Array a new method!




Want to improve your JavaScript? I have a list of recommended JavaScript books.