Part 2: The JavaScript Pseudo-Classical Pattern Explained (with pictures)

In Part 1 we discussed the OLOO Pattern: Objects Linking to Other Objects in JavaScript. We learned that objects aren’t created from classes, but rather from other objects called prototypes. Prototypes can then receive “requests” from objects at the bottom of the prototype chain in order to store common data and behaviour. Once prototypes are understood, OLOO can be a very simple and dynamic pattern to create objects with.

The predecessor to OLOO is the Pseudo-classical pattern. It is known to be a more complex system, where constructor functions and prototypes work together to create objects.

Now you might be wondering why a more complex pattern precluded the OLOO pattern. Douglas Crockford — an ongoing influencer in the development of JavaScript — explains there were many mixed feelings about JavaScript’s prototypal nature. This was due to the fact that all popular languages featured the “classical approach” — classes creating objects (as demonstrated in our Ruby example in Part 1).

There are a couple of key items that class-based languages often represent:

  1. Some sort of constructor function for creating objects
  2. A new keyword which initializes the creation of a new object

To keep the syntax familiar, the first release of JavaScript (ES1) used constructor functions and the new keyword to create objects. Under the hood however lies JavaScript’s true nature: a system of objects and prototypes secretly linking up to each other.

Constructor Functions, Prototypes and Objects

To explain how this all works lets go back to our myChair object we created in Part 1:

the myChair object

Firstly, below is an object literal that produces a simple chair like the above:

let myChair = {
width: 50,
minHeight: 45,
};

And here is the pseudo-classical approach to creating the same chair:

function Chair(width, minHeight) {
this.width = width;
this.minHeight = minHeight;
}
let myChair = new Chair(50, 45);
myChair; // { width: 50, minHeight: 45 }

Based on this code we can come to some conclusions:

  • The Chair function is invoked with the new operator, which turns an ordinary function into a ‘constructor function’ (we will explain constructors later). Constructors are typically capitalised to differentiate themselves from normal functions and other variables.
  • Within the Chair constructor this sets width and minHeight properties to itself, and assigns passed-in values to these new properties (again this will be explained later).
  • An object is returned from the method as indicated on the last line when we reference the object assigned to myChair —it returns the same value as our object literal.

Okay… this kind of explains what’s happening with a constructor ‘instantiating’ an object, but what about Pseudo-classical inheritance? Does our myChair object inherit from anything?

Actually it does! But to explain inheritance we must first understand that…

All functions are objects — they have properties and methods just like regular objects!

Functions are actually objects! Now that we know this, we can finally understand how Pseudo-classical inheritance works:

Our Chair constructor function —actually all functions for that matter — have a special property called the prototype property that references...yup you guessed it — a prototype object. It’s called Chair.prototype.

typeof Chair.prototype;    // "object"

This Chair.prototype object supports prototype-based inheritance in JavaScript. The myChair object produced from our Chair constructor function will reference this Chair.prototype object in its own hidden [[Prototype]] property:

under the hood of a constructor, JavaScript implements prototype-based inheritance

Now you might be wondering, “why would JavaScript go through all this trouble just to link two objects together using a constructor function?!” It was to make JavaScript feel more approachable to programmers who were more familiar with class-based programming. Hence why this pattern is called Pseudo-classical. JavaScript is and always has been a prototype-based language. It has no true classes of its own, and can only use objects and prototypes to perform prototype-based inheritance.

the myChair prototype chain

Please note that a constructor function’s prototype property and an object’s hidden [[Prototype]] property are two different things. In this example however they both reference the same object — Chair.prototype. And as we learned in Part 1, all objects by default have Object.prototype — the most basic object — as their prototype.

The 5 Steps of JavaScript Constructors

Lets now have a closer look at how the constructors work, using our Chair example. If we remember our Pseudo-classical implementation from before:

function Chair(width, minHeight) {
this.width = width;
this.minHeight = minHeight;
}
let myChair = new Chair(50, 45);

When new Chair(50,45) executes, the following happens:

  1. Chair becomes a constructor and creates a new blank object:
    There’s nothing special about this blank object — think of it as a plain old empty object {}.
  2. The blank object’s hidden[[Prototype]] property is set to reference the Chair.Prototype object:
    As mentioned before, this is so our chair object can now receive prototype-based inheritance.
  3. The execution context (this) is set from the global object to the blank object:
    Because the Chair invocation had no explicit caller, by default this references the global object instead of our blank object. Before we can assign properties to our blank object, this must first point to it.
  4. Execute the function to add properties to the chair object:
    Our constructor function is finally executed, and all code within the definition runs. In this case, both passed-in arguments width and minHeight are assigned to properties of the same name, and set to our new chair object.
  5. The new chair object is returned from the Chair constructor function:
    By default, constructors will implicitly return the object created in step 1, unless an explicit return is used to return some other object.

The below animation shows how all 5 steps of constructors work:

Setting Behaviours to Pseudo-classical Objects

Now that you understand how constructors really work — congratulations! It is no small task!

There is just one final part of the Pseudo-classical pattern we have to discuss, and that is where prototypal inheritance comes into play.

Say we wanted to make 2 chair objects: myChair and anotherChair , each with different width and minHeight properties. These properties differ from one chair object to another so it makes sense to keep these differences on each object:

function Chair(width, minHeight) {
this.width = width;
this.minHeight = minHeight;
}
let myChair = new Chair(50, 45);
let anotherChair = new Chair(40, 60);
myChair; // { width: 50, minHeight: 45 }
anotherChair; // { width: 40, minHeight: 60 }

But what about properties that remain the same for all chair objects?

Behaviours are usually common amongst objects of the same type. Say for example, all Chair objects had the ability to be folded and put away. We could create a fold method like so:

function fold() {
console.log('chair folded!');
}

Great! Now that we have a fold function how do we attach it to all of our chair objects?

We could pass this function object to the constructor and assign it like the other properties, e.g. this.fold = fold;. However this would cause unnecessary duplication, as each chair object would hold the exact same method. As we learnt in Part 1: objects should keep only what remains unique to them and delegate common data to their prototype.

So that’s exactly what we will do! We can add our fold function as a method to Chair.prototype — the prototype of all chair objects:

Chair.prototype.fold = function() {
console.log('chair folded!');
}

Now if either chair object — or any future chair object for that matter — should invoke their fold method it is found on their prototype chain within the Chair.prototype object. It is accessible via each chair’s hidden [[Prototype]] property:

chair objects inherit the fold() method from their prototype

Conclusion

Phew! There is a lot involved when it comes to the OLOO and Pseudo-classical object creation patterns — so I’m glad you stuck it out to the end!

Here are some important points to remember about Object creation in JavaScript:

  • JavaScript is an object-oriented programming language in the purest sense: it has no classes of its own, and simple objects can become working prototypes for other objects
  • JavaScript supports prototype-based inheritance: inheritance can only be performed using objects and prototypes. Anything else that claims to look like inheritance is just objects working under the surface
  • Objects delegate shared, common properties to their prototypes using a prototype chain: this way, objects only contain the necessary properties that makes them unique, keeping them small and efficient

I hope you found this pair of articles insightful and thank you so much for taking the time to read it. Happy studying!

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store