JavaScript Design Patterns: Building a Mental Model

Photo by Daniil Kuželev on Unsplash

Introduction

One of the most difficult aspects of learning JavaScript is getting your head around objects and all the associated terminology. Prototypes, constructor functions, constructor properties, dunder protos, prototype chain etc etc. It can very easily become overwhelming when you try to take it in too quickly.

This article will take you through JavaScript’s object-oriented approach one small step at a time so you can build your mental model slowly and incrementally. It is written for those who have recently encountered the wonders of JavaScript objects and are struggling with the associated concepts and terminologies.

Prerequisite Knowledge

To get the most out of this article it is expected that you are familiar with basic JavaScript syntax and data types. It would also help if you have previously encountered objects and this. If you don’t know what this is, fellow Launch School student Severin Perez has written a great article.

Developing a Mental Model

We’ll start our mental model with a picture that might well be familiar. You may have it or an equivalent before regarding inheritance related analogies.

Here we have a Terrier class that inherits from a Dog class, which in turn inherits from an Animal class. I know, it’s tedious, but bear with me.

Now, forget everything you know about class based object-oriented programming. This is different. We have no classes, only objects. Each object is linked to another object. So we can redraw the above diagram to represent this ‘linking’ concept.

Here the terrier object is linked to the dog object, which in turn is linked to the animal object. But how are they linked? Every object in JavaScript has an internal [[prototype]] property that can be accessed directly by a __proto__ property. This property, referred to as the ‘dunder proto’, essentially points to another object. It can be thought of as a link to another object. With that in mind, let’s redraw our diagram!

Okay great. We can see that the __proto__ property is pointing to another object. But what does that mean for us?

The Protoype Chain

The Prototype Chain, or Prototypal Inheritance, is how JavaScript ‘inherits’ properties from other objects. If we try to access a property on an object and it’s not a property owned directly by the object in question, the next port of call is the object pointed to by the __proto__ property. We can create an object that is linked to another object by using Object.create(obj). This will create a new object, who’s __proto__ property is set to point at obj, the object passed in as an argument. Let’s see this with some code.

So we can see clearly the relationship between dog and animal. Even though there are no properties defined directly on dog, we were still able to access type. Let’s create our third object, terrier from dog. Notice that our terrier object will also inherit the properties defined on the animal object.

Note: The code blocks from here on can involve quite a bit of repetition. The repeated code has been commented out to help you focus on the changes/additions.

We can see from the above that terrier.__proto__ clearly points to dog and dog alone. There are a couple of methods that let us query this prototypal relationship of one object to another.

The first, Object.prototype.isPrototypeOf() is used to check if one object exists anywhere on the prototype chain of another object. So for our three example objects, we can see from our diagram above that animal is on the prototype chain of both dog and terrier, while dog is on the prototype chain of terrier. Let’s test this assumption…

Okay so that adds up. But what if we want to find out if dog.__proto__ is linked directly to animal rather than being on it’s prototype chain. Well we could just use dog.__proto__ === animal, which in this case will return true. This __proto__ property, however, has only been recently standardized and is actually listed as deprecated due to some potentially problematic behavior. There is another way to query this relationship and that is by using Object.getPrototypeOf(obj).

So what happens if we add a property to dog after we create terrier? Will this property be available to terrier?

It doesn’t matter if properties are added to an object’s prototype chain after the object has been created. Here terrier.__proto__ actually points to dog, not to a copy of dog. Any changes made higher up the prototype chain will be reflected further down.


OLOO

Objects linked to other objects (OLOO) is a JavaScript Design Pattern that lets us define a parent object from which we can create other objects. All shared properties will be defined on this parent object. There is a convention used in JavaScript where the first letter of this parent object will be capitalized. That’s all it is though, a convention. At the end of the day, it’s just another object. Let’s redefine our mental model to reflect this convention.

Now let’s say we want to create a couple of different objects from Animal. We’ll create a reptile object and a mammal object. Here is an overview of what we are trying to achieve:

We would like these objects to share a common breathe property but have different type properties. One will be .type = "mammal" and the other will be .type = "reptile”. Let’s include the property names in our mental model to get a better picture of what we’re trying to do.

Great, now let’s see it with code…

You can see that we have a new method, init, defined on Animal. This method is used to initialize values in our newly created objects. So on line 14 we create a new object, mammal. Since mammal was created from Animal, it’s __proto__ object will point to Animal. This in turn means it will have access to the properties defined on Animal, including the init method. There is nothing special about this method, it can be called whatever you like but the convention is to use init.

If we follow the method call on line 15 to line 3, the important thing to note is that this will be the object that the method is called on, in this case mammal. So line 3 will be the equivalent of mammal.type = "mammal", "mammal" being the argument passed in. Similarly, when line 17 executes, we can follow to line 3, which will be be equivalent to reptile.type = "reptile".

Because our init method was defined on Animal, it will now also be available to Dog and Terrier due to prototypal inheritance. So if we create a new object from Terrier, called rex for example, rex will also have this init method available. A quick visual and then we’ll see it with code.

So there you have it, the nuts and bolts of JavaScript’s OLOO design pattern. This design pattern is probably the simplest design pattern and it embraces JavaScript for it’s prototypal inheritance.

There is another popular design pattern, the Pseudo-Classical Pattern that uses functions to create objects. It can be difficult for new developers to understand the nuances of this approach. Looking at diagrams and mental models can look like a spider’s web. We will therefore build up a mental model slowly, one step at a time. It’s important that you fully grasp each addition to the mental model and code snippet before moving forward or you will quickly find further additions overwhelming.


The Pseudo-Classical Pattern

Let’s keep things simple and start out with our basic mental model of class based inheritance.

This is the approach that the Pseudo-Classical Pattern tries to emulate. It does so by creating objects using functions rather than classes. We can see from the following factory function how easy it is to create an object from a function.

There are two major drawbacks to this factory function approach. The first being we have no way to identify what function created a particular object. In the OLOO approach we could do Object.getPrototypeOf(rex) === Terrier. In class based languages we can easily query an object to find out it’s class. We need a way to be able to identify what function created our JavaScript object.

The second drawback comes down to the use of system resources. In our code snippet above we can see that each object returned from createAnimal has it’s very own breathe() method. This is all well and good for our little snippet above but imagine a situation where your returned object has hundreds of methods. Additionally, your function could be used to create hundreds of these objects. That’s a lot of resources being wasted copying every method to every object. In the OLOO approach we had a link to a parent object that contained the shared methods. It would be nice to have a similar approach.

new Function()

There is a keyword new in JavaScript that when used before a function call will create a new object and execute all the code in that function. Any reference to this within the body of the function will point to the new object. Finally, this new object will be returned if no other object is explicitly returned. Let’s see this with some code.

Seeing as our Animal function is used to construct objects, we’ll refer to it as a constructor function. Notice, again, the capitalization of Animal. Like the object names in the OLOO approach, this is by convention only and is to distinguish constructor functions from ordinary functions.

When we execute new Animal() on line 9, a new object is created within our constructor function. Lines 2 & 4 set properties on this new object and finally the object is implicitly returned. This returned object is assigned to animal on line 9. Let’s have a look at one of the benefits of using this approach. Addressing the first drawback of using factory functions we now have access to a property called .constructor that will kindly tell us the name of the function that created our object.

Interesting. That’s a nice property to have but where exactly did it come from? Let’s have a look at the properties defined directly on our animal object. We can do this by using the method Object.getOwnPropertyNames(obj).

Hmm, so if constructor is not owned by animal then it must be defined on animal`s prototype chain. So where is animal.__proto__ pointing to?

Okay, so animal.__proto__ is pointing directly to an object containing a constructor property. So now the question is where is this object located and what is it called? The name of the property that points to this object happens to be a very unfortunately named one that can cause much confusion for new developers. This property is defined on our Animal function. In JavaScript functions are themselves objects with their own properties. One of those properties is prototype and it’s value is the previously found object containing the constructor property. So for our example above, our constructor function is Animal. Animal has a prototype property. The value of this prototype property is an object containing the constructor property.

Look familiar? Indeed Animal.prototype is the object pointed to by animal.__proto__. Very importantly, this link happens when the keyword new is used to create an object from a constructor function.

So the last thing we need to establish is the value of the constructor property in this object {constructor: f}. We can see it’s a function but what function is it?

Ah yes, it’s the same function that we saw back in example10.js above when we called animal.constructor. So our constructor property here simply points back to our original constructor function Animal. A big advantage of this is the very fact that it allows us to interrogate objects to find out the name of the function that constructed them, as shown in example10.js.

Phew, that was a lot to take in and admittedly it can be difficult to follow without some visual aid. Let’s rectify that and update our mental model. Below is the code snippet we’ll use to start.

Things can get pretty crazy pretty quickly trying to map out mental models of the Pseudo-Classical Pattern so we’ll take it slow and build it up one step at a time. First, we know that each function is itself an object and has a property named prototype.

We also know that the value of this prototype property is an object containing a constructor property.

We discovered that this constructor property points back to the constructor function so let’s add this detail as well.

Now we’ll use our Animal constructor function to create a new mammal object.

We know that the __proto__ property of mammal is now pointing to the same object referenced by Animal.prototype. Let’s see it…

We can now use this fact to define methods on the object referenced by Animal.prototype. We do this so the methods will be shared by every object created byAnimal . All objects that are created byAnimal will have their __proto__ property set to reference this object, thus addressing the second disadvantage of using factory functions discussed earlier.

Let’s test this out this concept of sharing methods by adding a breathe property to Animal.prototype .

And updating our mental model…

Now we can add the code to the Animal constructor function that will create the type property on our mammal object.

Now every time a new object is created by Animal, the code within the body of the function will execute, with the new object taking the place of this. The result will be the property type: "mammal" being defined on our new mammal object.

Note: We can define our constructor functions to take any number of arguments. This would allow us to pass in different values to be assigned to this.type . We’ll keep things simple for now though and have every new object’s type set to "mammal" .

The next thing we need to look at is how the concept of inheritance is emulated in the Pseudo-Classical Pattern. We can expand our code to include constructor functions for Dog and Terrier.

Now the pressing question is… How do we link these functions together? We want to be able to create a dog object from Dog that will inherit from Animal. Similarly, we want to be able to create a terrier object from Terrier that will inherit from Dog.

There are two ways to set up this hierarchical approach. The first way lets us inherit all the properties and methods that a new object created from the parent constructor function would have access to (the body of the function is executed). The second let’s us inherit only the properties that have been defined on the parent constructor function’s prototype object (properties defined in the body of the function will not be inherited).

Let’s set out to do both. First, we’ll have Dog inherit breathe() from Animal while also having the property type defined on it. We’ll then look at setting up a relationship between Terrier and Dog. There’s quite a bit in this so we’ll take it one step at a time.

Setting the Prototype to Point at a new Function

We want our Dog constructor function to have access to all the properties that would be available to an object created from the Animal constructor function. We know that objects created from a constructor function will have access to all the properties defined in the constructor function’s prototype object. We can see this in the above diagram, mammal.__proto__ is pointing at Animal.prototype. So let’s try and make Dog.prototype point to an object that will give us access to the required prototype chain.

We can see in line 12 of example20.js that we used new Animal() to create a new object that had access to all the properties we want Dog to have access to. Let’s do the same again but this time we’ll get Dog.prototype to point to this new object. Have a look at the code below followed by our updated mental model.

Notice how we just redirected Dog.prototype to point to a new object. The old object that contained the .constructor property is now no longer referenced so will be garbage collected.

So now if we create a dog object, it’s prototype chain should be linked to this new object.

dog should now have access to the type and breathe properties.

Great. Now, there’s one more loose end we need to tie up. Can you see what it is? Let’s try and find out what constructor function created dog.

Hmmm, that’s not quite right is it? We want our dog.constructor to point back to Dog, not Animal. Remember we reassigned Dog.prototype to point to a new Animal object? The result was that there was nothing referencing the old object containing the constructor property so it was garbage collected. So now we must manually add a constructor property to Dog.prototype.

There we go, much better. We can easily identify what constructor function was used to create dog. Now that we have our Dog constructor function set to inherit from Animal, let’s set up a relationship between Terrier and Dog. Remember, we only want objects created from Terrier to inherit behaviors or methods defined in Dog.prototype so we’ll set this relationship up using a different technique.

Setting the Prototype to point at Object.create(obj)

We know all about Object.create(obj) from reading about the OLOO Pattern way back near the start of this article. It will create a new object and set this new object's __proto__ property pointing back at the object passed in as an argument. To help illustrate the difference between this method of setting up a hierarchical relationship and the last, we’re going to add a line of code to the body of the Dog constructor function, this.legs = 4. This is to illustrate that this property will not be inherited by Terrier in the same way that type was inherited by Dog.

Our mental model is admittedly getting very cluttered so we’ll omit the dog and mammal objects. Have a look at the code below, specifically the last line, where we set Terrier.prototype to point to a new object created from Dog.prototype.

Now, as before, we have redirected Terrier.prototype to point to a new object. It has therefore lost any reference it had to a .constructor property. We will therefore need to manually add it back.

Inspecting the mental model above and following the prototype chain, we can see all the properties Terrier.prototype has access to. It has a constructor property . It also has a __proto__ property pointing to Dog.prototype, which has a type property. And finally, Dog.prototype.__proto__ points to Animal.prototype, on which breathe has been defined.

Notice here that this prototype chain does not have access to a legs property. If we create a new object from Terrier it will have access to this aforementioned prototype chain, which again does not have access to legs. Let’s create a new object, rex, from Terrier and test this out.

Let’s see what properties rex has access to.

This illustrates the key difference between setting the constructor function’s prototype equal to objects created with new and setting it equal to objects created with Object.create(obj). With new we’re causing the code within the constructor function to run and creating a link with a prototype chain but with Object.create(obj) we’re simply creating the link without executing the code in the constructor function .

Another useful benefit of the constructor property is that we can use it to create new objects. We know that it points back to a constructor function so let’s say we have an object, rex, and in this case we don’t know about Terrier. We want to create another object similar to rex. We can access rex.constructor and call new on it. This is the equivalent of calling new Terrier();


Where Does it End?

If you’ve made it this far, give yourself a pat on the back. We’ve covered the nuts and bolts of the two most common JavaScript design patterns. Feel free to call it a day and let your brain go to work absorbing all this information. However, if you’d like to delve a little bit further into the prototype chain and find out where it ends, read on.

If we follow the prototype chain in our mental model, we can see that it ends up at Animal.prototype. For the sake of clarity, we will concentrate on the portion of our mental model that contains the Animal constructor function. We know that the prototype chain finishes there so there’s no need to clutter up our screen with excess information.

We know Animal.prototype is an object. We can see that it contains the properties constructor and breathe. As we stated much earlier, however, all objects have a __proto__ property. So what is this object’s __proto__ property and where does it lead to?

Hmmm, it’s not immediately obvious what this object is. We can see that this unknown object has a constructor property though. Let’s see where that leads us.

Okay, so the constructor property points back to an Object constructor function.

That unknown object is starting to look a lot like the Object constructor function’s prototype object. Let’s see…

This Object constructor function is used by JavaScript to create objects. Calling this constructor function with the keyword new will return an empty object, {}.

Okay so we’re nearly at the finish line, let’s follow the chain. Our previously unknown object that we’ve now identified as being Object.prototype also has a __proto__ property.

In JavaScript, null represents the intentional absence of any object value. It is quite fitting, therefore, that null is found at the end of our prototype chain.


Summary

There is undoubtedly a lot to absorb in this article, especially if you’re new to JavaScript. Here’s a quick recap to help drive home the key concepts.

  • JavaScript does not have classes in the traditional sense. Prototypal inheritance is used to link objects together.
  • Every object in JavaScript has a __proto__ property. If a property is not found on an object it will check the object referenced by this __proto__ property.
  • Object.create(obj) is used to create a new object and link it’s __proto__ property to the object passed in as an argument (obj ).
  • Any properties defined anywhere on an object’s prototype chain will be available to said object.

OLOO:

  • Shared properties are defined on a parent object. Other objects can then be created from this parent object using Object.create(obj) .
  • An init() method defined on the parent object is used to initialize newly created objects with properties. This method is optional but commonly used.

Pseudo-Classical:

  • New objects are created from constructor functions using the keyword new .
  • Calling new on a function creates a new object. The code within the function executes with the execution context (this) set to the new object. The newly created object’s __proto__ property is set to point at the object referenced by the functions prototype property. The newly created object is then implicitly returned.
  • obj.constructor can be used to find out the name of the constructor function that created an object.
  • Inheritance can be emulated by changing where a functions .prototype property points to (Just remember to reset where the .constructor property points to).

That concludes this basic introduction to JavaScript Design Patterns. Don’t worry if you didn’t fully grasp all the concepts here. It can take a few attempts and a bit of practice to understand what’s happening and for everything to click. While there is a lot more to learn with regards to implementing these design patterns, if you can comprehend the topics covered here you’ll be well on your way.

If you have any feedback/criticisms you’re more than welcome to leave a comment below. I’d love to hear from you.