Object Oriented JavaScript Cheat Sheet

Object Oriented JavaScript Cheat Sheet

JavaScript Objects

Javascript objects can be created using new Object() or by object literal notation (preferred) and whatever properties and methods (functions within an object) we wish added.

car = new Object();  
  
car.manufacturer = "Bugatti";
car.model = "Veyron";
car = {
    manufacturer: "Bugatti",
    model: "Veyron"
}

Unlike JavaScript primitives like string or number, objects are addressed by reference. This means that assigning an existing object to another variable will not make a copy. The new object will just point to the same object that the original one does. Changing either of these objects will change both of them.

anotherCar = car;  
  
anotherCar.manufacturer = "McLaren";
anotherCar.model = "P1";  
  
console.log( anotherCar ); // Object {manufacturer: "McLaren", model: "P1"}
console.log( car ); // Object {manufacturer: "McLaren", model: "P1"}

We can use Object.create() to make new objects that inherit all properties and methods from an existing object. However we still have to replace/set all the values of the new object so no real advantage to this approach. We may as well just create new objects from scratch.

anotherCar = Object.create( car );  
  
anotherCar.manufacturer = "McLaren";
anotherCar.model = "P1";  
  
console.log( anotherCar ); // Object {manufacturer: "McLaren", model: "P1"}
console.log( car ); // Object {manufacturer: "Bugatti", model: "Veyron"}

So what if we want lots of different objects that are of the same type? What is the best approach to handling object creation?

Object Constructor Functions

All built-in objects (with the exception of Math) have their own constructors. When we use the new keyword, we are telling JavaScript to use the constructor of the following object ie. new Date(). Constructors may also receive arguments to be used within the construction process.

A constructor function is similar to a class in other OO languages in that we can define properties to store our data, and methods to perform operations on this data. It is accepted convention in JavaScript to always capitalise the first character of a constructor function name to help differentiate it from regular functions.

function Car( manufacturer, model, wheels ) {
    this.manufacturer = manufacturer;
    this.model = model;
    this.wheels = wheels;
    this.start = function() {
        console.log( "Nice " + this.manufacturer + " " + this.model + ", let's roll!" );
    };
}

Note the use of the this keyword. In the context of a constructor function (when called with the new keyword), this relates to the object being created. Also note that we are not returning anything either as the properties and methods are being defined directly on this object.

We can now create/instantiate new objects with our constructor and pass in the values to be set.

var modelS = new Car( "Tesla", "Model S", 4 );  
  
console.log( modelS ); // Car {manufacturer: "Tesla", model: "Model S", wheels: 4, start: function}
modelS.start(); // Nice Tesla Model S, let's roll!

Objects have a constructor property which is automatically set with a reference to the constructor function used to create it. We can check this with the following methods:

console.log( modelS.constructor == Car ); // true
console.log( modelS instanceof Car ); // true

However, a problem remains that new objects all have a copy of all the properties and methods, regardless of whether or not they will every need or use them. What if we could keep certain properties and methods in a central place so that all instances of an object could access them only when required?

Object Prototypes

Every object has a prototype property which, as it sounds, is a kind of template that an object's constructor uses when creating new instances of itself.

Every built-in object (with the exception of Math which doesn’t have a constructor) has a prototype property with its own properties and methods defined within. Thus an instance of Array or Date for example inherits a multitude of methods from its constructor's prototype without carrying the weight of these within the instance itself. Objects created using new Object() or object literal notation directly inherit properties and methods from Object.prototype such as constructor, hasOwnProperty(), toString(), etc.

We can define our own prototypes for our constructor functions and move any properties or methods that we wish to share over to the prototype. Let's rewrite our Car example:

function Car( manufacturer, model, wheels ) {
    this.manufacturer = manufacturer;
    this.model = model;
    this.wheels = wheels;
}  
  
Car.prototype.start = function() {
    console.log( "Nice " + this.manufacturer + " " + this.model + ", let's roll!" );
};  
  
var modelS = new Car( "Tesla", "Model S", 4 );  
  
modelS.start(); // Nice Tesla Model S, let's roll!

Note that we could have defined the prototype with object literal notation. This has the advantage of being able to define multiple properties and methods with less code. However, as this effectively replaces the whole property, we have to redefine the constructor property.

Car.prototype = {
    constructor: Car,
    start: function() {
        console.log( "Nice " + this.manufacturer + " " + this.model + ", let's roll!" );
    }
}

Objects also have a prototype attribute or object (not to be confused with the prototype property above) which is automatically set and shows where an object descends from i.e. its parent. The attribute stores a reference to the constructor's prototype. So for example a new Object() would descend directly from Object.prototype, an object instantiated from new Date() would descend from Date.prototype, and in our Car example an object would descend from Car.prototype. We can use the following methods to check which prototype was used to create an object:

console.log( Car.prototype.isPrototypeOf( modelS ) ); // true
console.log( Object.getPrototypeOf( modelS ) === Car.prototype ); // true (IE9+)

Inheritance and the Prototype Chain

JavaScript objects can inherit from other objects, which can in turn inherit from other objects, and so on. If we create a new Date object for example, this would inherit from Date.prototype which in turn inherits from Object.prototype, creating a chain of inheritance. All objects, be they custom or built-in, ultimately inherit from Object.prototype.

When a property is referred to, or a method is called, JavaScript will firstly check in the current object to see if that property or method is defined. If it isn't, the object's prototype attribute is looked up (following the prototype chain) and the object defined there is then checked. This chain lookup continues until a matching property or method is found.

There are several different approaches/patterns used to implement inheritance for our own JavaScript objects, each with their own pros and cons. Let's look at two of the most popular patterns using our Car example again and add in a new parent object type Vehicle with its own properties and methods.

Combination Inheritance

We saw earlier that we can create an object via a constructor function, and we can also add properties and methods to the constructor's prototype that we wish to share with all child objects. We run into problems though if any of the constructor properties hold a composite value such as an array or an object. Primitive values such as string or number are fine because the value will likely be replaced by the instantiated object. Composites however are only references to an object. This means that any instantiated objects would inherit the reference to the same property object. Altering that property value, such as adding an item into an array, would change the referenced object and so change the value for all child objects. So what can be done?

This pattern is so called because it combines constructor stealing with prototype chaining. It is an attempt to replicate in some ways a classical inheritance pattern found in many other Object Oriented languages. Basically, we use prototype chaining to inherit the properties and methods defined on the constructor's prototype and we inherit the instance properties (defined in the constructor itself) by stealing the whole constructor. In practice we achieve this by setting the Car prototype attribute to inherit from the Vehicle.prototype (ensure this is done before defining any prototype properties or methods on Car). Then we tell the Car constructor to call (steal) the Vehicle constructor so inheriting it's properties and methods.

function Vehicle( colour ) {
    this.purpose = "To move things from A to B.";
    this.colour = colour;
}  
  
Vehicle.prototype.howManyWheels = function() {
    console.log( "I'm rolling on " + this.wheels + " wheels baby!" );
}  
  
Vehicle.prototype.whatColour = function() {
    console.log( this.colour + " is the best colour by far!" );
}  
  
function Car( manufacturer, model, wheels, colour ) {
    // Call the parent constructor. Note we can now pass arguments too. Woop!
    Vehicle.call( this, colour );  
  
    this.manufacturer = manufacturer;
    this.model = model;
    this.wheels = wheels;  
  
}  
  
// And we inherit the parent prototype
Car.prototype = Object.create( Vehicle.prototype );  
  
Car.prototype.start = function() {
console.log( "Nice " + this.manufacturer + " " + this.model + ", let's roll!" );
};  
  
var robin = new Car( "Reliant", "Robin", 3, "Risky Red" );  
  
robin.start(); // Nice Reliant Robin, let's roll!
robin.howManyWheels(); // I'm rolling on 3 wheels baby!
robin.whatColour(); // Risky Red is the best colour by far!  
  
var veyron = new Car( "Bugatti", "Veyron", 4, "Storm Silver" );  
  
veyron.start(); // Nice Bugatti Veyron, let's roll!
veyron.howManyWheels(); // I'm rolling on 4 wheels baby!
veyron.whatColour(); // Storm Silver is the best colour by far!  
  
console.log( Car.prototype.isPrototypeOf( robin ) ); // true
console.log( Vehicle.prototype.isPrototypeOf( robin ) ); // true

Prototypal Inheritance

Whilst the Combination Inheritance pattern is very popular, JavaScript is a class-less language and we can implement inheritance perfectly well without classes or constructors. As we know, objects can inherit from other objects, which can in turn inherit from other objects, and so on. Not a class in sight! This form of inheritance is very easy to understand compared with some other patterns. We can use a mix of object literal notation and the Object.create() method to make our objects which makes our code easier to read. However we need to define every property of every object we create rather than just passing the values in as arguments to a constructor.

var vehicle = {
    purpose: "To move things from A to B.",
    colour: "Rainbow",
    howManyWheels: function() {
        console.log( "I'm rolling on " + this.wheels + " wheels baby!" );
    },
    whatColour: function() {
        console.log( this.colour + " is the best colour by far!" );
    }
};  
  
  
var car = Object.create( vehicle );  
  
car.manufacturer = "";
car.model = "";
car.wheels = "";
car.start = function() {
    console.log( "Nice " + this.manufacturer + " " + this.model + ", let's roll!" );
};  
  
  
var robin = Object.create( car );
robin.manufacturer = "Reliant";
robin.model = "Robin";
robin.wheels = 3;
robin.colour = "Risky Red";  
  
robin.start(); // Nice Reliant Robin, let's roll!
robin.howManyWheels(); // I'm rolling on 3 wheels baby!
robin.whatColour(); // Risky Red is the best colour by far!  
  
  
var veyron = Object.create( car );
veyron.manufacturer = "Bugatti";
veyron.model = "Veyron";
veyron.wheels = 4;
veyron.colour = "Storm Silver";  
  
veyron.start(); // Nice Bugatti Veyron, let's roll!
veyron.howManyWheels(); // I'm rolling on 4 wheels baby!
veyron.whatColour(); // Storm Silver is the best colour by far!

Note that both of the above patterns make use of Object.create(). This method has only been a native to JavaScript since the definition of ECMAScript 5 (thanks to Douglas Crockford). This means the method is not available to older browsers such as IE8 or older. We can however create the method if it isn't already defined:

if ( typeof Object.create !== 'function' ) {
    Object.create = function ( o ) {
        function F() {}
        F.prototype = o;
        return new F();
    };
}

Shadowing And Deleting

So what happens if an object defines a property or method with the same name as that of one of its ancestors? The descendant's property or method would shadow the ancestor's and stop the chain lookup right there. Other objects that also inherit from the same ancestor but have not shadowed the same property or method, will still use that of the ancestor.

robin.start = function() {
    console.log( "Three wheels is the only way to travel!" );
};  
  
robin.start(); // Three wheels is the only way to travel!  
  
veyron.start(); // Nice Bugatti Veyron, let's roll!

To allow an object to inherit the property or method again, we must do more than just set the value to null or undefined. The delete operator must be used to completely remove the shadowing property or method.

delete robin.start;  
  
robin.start(); // Nice Reliant Robin, let's roll!