Skip to main content

A Comparative Guide to JavaScript Object Creation Patterns

·9 mins

If you started learning programming with a traditional class-based language, JavaScript’s Object Oriented Programming might look confusing at first. You will find a lot of articles on different object creation patterns which might be a bit overwhelming. In this article, we will analyze 4 common object creation patterns. I will start with a brief review of some of the underlying concepts/features. Then, I will examine some code samples and discuss how each pattern works using some visual aid.

The aim of this article is to build up how each object creation pattern implements prototypal inheritance in steps and solidify your mental model. Let’s start with some background.

Inheritance in JavaScript #

The term inheritance in OOP commonly refers to class-based languages where there is a sub-class/super-class relationship, being sub-classes inherit behaviour and data from super-classes.

JavaScript does not have true-inheritance as we know it from class-based languages. Instead, it uses something called prototypal inheritance. When we create an object, say child from another object called parent , child references it’s properties and methods to parent object’s prototype. This way it can delegate an access request up in the prototype chain which is why we call this behavior delegation.

Comparison of prototype and Hidden [[Prototype]] Properties #

Properties of functions and objects | Key on top-right demonstrates object types

  • In JavaScript, prototype property only exists in Functions. It is basically an object that Function returns when invoked as a constructor with the new keyword.
  • [[Prototype]] is a property that all objects contain. This property points to prototype object that the initial object created from.

Let’s look at the above diagram to see what happens when we create an empty function vs an empty object.

On the left, we have the Sample function. When we create a function, it initially inherits from Function.prototype which is referenced to it’s [[Prototype]] property. Functions also contain a prototype property that contains a prototype object. This object contains a property called constructor that refers back to the function. We will make use of constructor later.

On the right, we have our object Sample with [[Prototype]] property. The value that [[Prototype]] points to depends on how the object is created. If it is created using the object literal syntax it will reference to Object.prototype . If the object is created by a constructor function, it will be referencing to prototype property of that function.

new Keyword in JavaScript #

Lastly, let’s remember what happens when we prepend a constructor function call with new keyword. Let’s say we have a construction function ConstFunc and we have a line of code like this: let newObj = new ConstFunc(arg1, arg2);

  1. When invoked, constructor function ConstFunc creates a new object
  2. this (execution context) assigned to this new object as part of the function call.
  3. ConstFunc is executed in this context with passed in arguments.
  4. If there is no explicit return statement, the value of this will be returned.

The created object will have the below relationship with the constructor function

  • newObj.constructor === [ Function: ConstFunc ]
  • ConstFunc.prototype === ConstFunc {}
  • newObj.constructor.prototype === ConstFunc {}

Now we covered all the basics, the rest will be quite straightforward.

Constructor Functions #

function Album(name = 'N/A', artist = 'N/A', year = 'N/A') {
  this.name = name;
  this.artist = artist;
  this.year = year;

  this.readTag = function() {
    console.log(this.name + ' by ' + this.artist);
    console.log('Released in ' + this.year);
  };
}

let inAbsentia = new Album('In Absentia', 'Porcupine Tree', '2002');

inAbsentia.constructor;                    // [Function: Album]
Album.prototype;                           // Album {...}
inAbsentia.constructor === Album;          // true

inAbsentia instanceof Album;               // true
Album.prototype.isPrototypeOf(inAbsentia); // true
Object.getPrototypeOf(inAbsentia);         // Album {...}
Album.isPrototypeOf(inAbsentia);           // false
Album.prototype.constructor;               // [Function: Album]

Object.getPrototypeOf(inAbsentia) === Album.prototype //true

Previously, we’ve discussed how constructor functions work while we explained how the new keyword works. The key takeaway from this pattern is Album.prototype. It is the prototype object for constructor function’s return value.

“Inheritance” mechanism of constructor functions

When we created the new objects using the constructor function Album , all shared methods are copied into instances of Album.prototype . Therefore, although [[Prototype]] property of the new objects point to Album.prototype , they will still use their own copy of the methods.

Factory Pattern #

function makeAlbum(name = 'N/A', artist = 'N/A', year = 'N/A') {
  return {
    name: name,
    artist: artist,
    year: year,

    readTag: function() {
      console.log(this.name + ' by ' + this.artist);
      console.log('Released in ' + this.year);
    },
  };
}

var inAbsentia = makeAlbum('In Absentia', 'Porcupine Tree', '2002');
inAbsentia.readTag();   // In Absentia by Porcupine Tree
                        // Released in 2002

var deadwing = makeAlbum('Deadwing', 'Porcupine Tree', '2005');
deadwing.readTag();    // Deadwing by Porcupine Tree
                       // Released in 2005

Factory pattern makes use of a function that returns an object when invoked. Above example makes use of the object literal syntax for object creation, but other ways such as new Object() can be also used.

“Inheritance” mechanism of factory functions

Factory pattern is computationally inefficient since all the methods will be copied to every single object that is returned. Unlike constructor functions it is not possible to track down how these objects are created, or how they are related to makeAlbum function. Also, it is not possible to update the “shared” behavior once the objects are returned by the factory function.

Pseudo-Classical Pattern #

let Album = function(name = 'N/A', artist = 'N/A', year = 'N/A') {
  this.name = name;
  this.artist = artist;
  this.year = year;
};

Album.prototype.readTag = function() {
  console.log(this.name + ' by ' + this.artist);
  console.log('Released in ' + this.year);
};

Album.prototype.type = 'Prog-rock';

let inAbsentia = new Album('In Absentia', 'Porcupine Tree', '2002');
let deadwing = new Album('Deadwing', 'Porcupine Tree', '2005');

inAbsentia.readTag();                   // In Absentia by Porcupine Tree
                                        // Released in 2002
deadwing.readTag();                     // Deadwing by Porcupine Tree
                                        // Released in 2005
console.log(inAbsentia.type);           // Prog-rock
console.log(deadwing.type);             // Prog-rock

Pseudo-classical pattern combines constructor function and prototype pattern. This popular pattern resolves the problem of inefficiencies that have been discussed on the previous two patterns and introduces prototypal inheritance. Pseudo-classical pattern achieves this by creating a distinction between private properties and shared properties/methods. These are separated into a constructor function and the constructor function’s prototype.

Prototypal Inheritance mechanism of Pseudo-Classical Pattern
JavaScript class is introduced with ES6. Essentially, this does the same thing as the pseudo-classical model, it is just a syntactic sugar. It improves the organization of the code and provides a constructor method.

class Album {
  constructor(name = 'N/A', artist = 'N/A', year = 'N/A') {
  this.name = name;
  this.artist = artist;
  this.year = year;
  this.type = 'Prog-rock';
  }

  readTag() {
    console.log(this.name + ' by ' + this.artist);
    console.log('Released in ' + this.year);
  }
};

Lastly, The Pseudo classical pattern can be combined in the constructor function:

let Album = function(name = 'N/A', artist = 'N/A', year = 'N/A') {
  this.name = name;
  this.artist = artist;
  this.year = year;

	if (typeof this.readTag !== 'function') {
		Object.getPrototypeOf(this).readTag = function() {
		  console.log(this.name + ' by ' + this.artist);
		  console.log('Released in ' + this.year);
		};
	}

	if (!this.type) {
		Object.getPrototypeOf(this).type = 'Prog-rock';
	}
};

let inAbsentia = new Album('In Absentia', 'Porcupine Tree', '2002');
let deadwing = new Album('Deadwing', 'Porcupine Tree', '2005');

inAbsentia.readTag();                   // In Absentia by Porcupine Tree
                                        // Released in 2002
deadwing.readTag();                     // Deadwing by Porcupine Tree
                                        // Released in 2005
console.log(inAbsentia.type);           // Prog-rock
console.log(deadwing.type);             // Prog-rock

The if statement checks if the “to be created” object has already a property called readTag or type . Since inAbsentia is the first object that has been created by Album constructor, this will evaluate true and the shared behavior will be defined on the object’s prototype. When we create other objects with the same constructor function, this block of code will be skipped by the if statement.

OLOO (Object Linking to Other Object) #

OLOO, is an object creation pattern based on creating new objects with Object.create method using prototype objects. It is a relatively simpler pattern since it is not dealing with constructors and prototype properties.

let Album = {
  name: 'N/A',
  artist: 'N/A',
  year: 'N/A',
  readTag() {
    console.log(this.name + ' by ' + this.artist);
    console.log('Released in ' + this.year);
  },
  init(name, artist, year) {
    this.name = name;
    this.artist = artist;
    this.year = year;
    return this;
  },
};

let inAbsentia = Object.create(Album).init('In Absentia', 'Porcupine Tree', '2002');
let unknownAlbum = Object.create(Album);

inAbsentia.readTag();                               //In Absentia by Porcupine Tree
                                                    // Released in 2002

console.log(Album.isPrototypeOf(inAbsentia));       // true
console.log(Album.isPrototypeOf(unknownAlbum));     // true
console.log(inAbsentia.constructor.prototype);      // Object.Prototype
console.log(Object.getPrototypeOf(inAbsentia));     // Album

Album.check = function() {
  console.log('I inherit');
};

inAbsentia.check();                                    // "I inherit!"

With OLOO, object creation and initialization occur at different times. The latter can be done by using an optional init method as shown above.

Because OLOO Pattern does not use constructors, examining inheritance with methods like Object.prototype.constructor will not work as expected and simply return Object.prototype (the top element in the prototypal inheritance hierarchy). Instead, isPrototypeOf and Object.getPrototypeOf could be used for such purpose. Also, we can not use instanceof because the right-hand operand has to be a function.

Above inAbsentia and unknowAlbum have different properties, but they share the same methods as Album.

Prototypal Inheritance mechanism of OLOO Pattern

This approach provides behavior delegation rather than copying of all methods at object creation time. Therefore, when we add a new method check into prototype object and call this method on the already created instance inAbsentia , this method call will be delegated to its prototype.

TL;DR #

There are a lot of good article’s on object creation patterns in JavaScript. In this article, I’ve tried to focus on the underlying principles first.

  • [[Prototype]] is a hidden property contained in all objects. It is referenced to initial object’s prototype object.
  • prototype is a functions prototype object that it returns when invoked with new keyword.
  • When an object is created invoking a constructor function the returned object has a constructor property that points to constructor function.

At the second part I’ve discussed and compared 4 common object creation patterns with code samples and diagrams. Here are some key takeaways:

  • Factory Pattern gathers object creation functionality in a single function, prevents repetition (more on DRY). Each function invocation creates a new object. It is inefficient, hard to trace the sources of the objects. Objects have a copy of their own methods.
  • Constructor Function, takes one step further from factory pattern by introducing a fake prototypal inheritance. Created object will still have their own copy of methods, but their source can be traced by using constructor property on the instances.
  • Pseudo-Classical Pattern is a combination of constructor function and prototype pattern. It resolves the above inefficiencies by introducing prototypal inheritance. It achieves this by separating private properties and shared properties into constructor function and it’s prototype.
  • OLOO(Object Linking to Other Object), is a simpler solution that uses a prototype object instead of a constructor function. It relies of creating new object using Object.create with prototype object passed in as argument.

We now know all these patterns but which is “the best”? My answer would be It depends… The latter two are better practices when it comes to building your code organization. However, the former two are also important to understand the principals behind prototypal inheritance and can save you some time when dealing with smaller problems.