DEV Community

Cover image for A Quick Guide to Instantiation Patterns in JavaScript
Mike Conner
Mike Conner

Posted on

A Quick Guide to Instantiation Patterns in JavaScript

Instantiation refers to the creation of an object. Following that, instantiation patterns refer to the myriad ways to create instances of objects! Lets learn about a few of those patterns today.

Functional

The functional pattern of instantiation is very easy-to-read, making it pretty common. This pattern creates an object and assigns both it's properties and methods in the same function call, which makes it easy for readers to interpret. Lets take a look:

// Functional instantiation
const FuncPerson = function(name, favFood) {
  const obj = {};

  obj.name = name;
  obj.favFood = favFood;

  obj.greet = function() {
    return `Hi, my name is ${name}!`;
  }

  obj.ask = function() {
    return `Would you like to get some ${favFood}?`;
  }

  return obj;
};

const tom = FuncPerson('Tom', 'pizza');
tom.name;  // returns 'Tom'
tom.greet();  // returns 'Hi, my name is Tom!'

First, we create a function, FuncPerson. Inside of that function, we declare an object, obj. Then we assign properties to that object. In this case, we create the properties name and favFood, and assign them values of the name and favFood properties passed into the FuncPerson function, respectively. Next, we create any methods we wish to make available to objects created with the FuncPerson function, .greet and .ask in our example. Finally, we return the object to complete our function. Invoking our function will create a new object with aforementioned properties (name and favFood) that has our created methods (.greet and .ask) available to it.

While simple, this instantiation pattern isn't perfect. Each invocation of our FuncPerson function will duplicate the methods that are created for each object. That is, if we create 5 person objects, we will create 5 different .ask methods.

Functional-shared

Functional-shared instantiation is similar to the functional instantiation pattern, but we don't suffer from the method-duplication problem seen in the functional pattern. Here we go:

// Functional-shared instantiation
const FSPerson = function(name, favFood) {
  const obj = {};

  obj.name = name;
  obj.favFood = favFood;

  _.extend(obj, fSPersonMethods);

  return obj;
};

const fSPersonMethods = {
  greet: function() {
    return `Hi, my name is ${this.name}!`;
  },

  ask: function() {
    return `Would you like to get some ${this.favFood}?`;
  }
};

const brad = FSPerson('Brad', 'spaghetti');
brad.name;  // returns 'Brad'
brad.ask();  // returns 'Would you like to get some spaghetti?'

Looks similar to the functional pattern, right? Again, we create a function with an empty object inside of it, assign properties to that object, and return that object. But there's two major differences: the call to _.extend and the location of our methods. We'll start with our methods object, fSPersonMethods. This just contains all the methods we want our new objects to have. This allows the _.extend function, part of the Underscore library, to copy the properties from our methods object into our new obj. Doing this solves our problem of method duplication.

Functional-shared seems simple, right? Prototypal instantiation is even simpler!

Prototypal

Instead of placing methods inside of an object, the prototypal instantiation pattern will place them on the object's prototype. To do this, we'll make use of Object.create. Lets look at an example:

// Prototypal instantiation
const ProtoPerson = function(name, favFood) {
  const obj = Object.create(protoPersonMethods);

  obj.name = name;
  obj.favFood = favFood;

  return obj;
};

const protoPersonMethods = {
  greet: function() {
    return `Hi, my name is ${this.name}!`;
  },

  ask: function() {
    return `Would you like to get some ${this.favFood}?`;
  }
};

const susan = ProtoPerson('Susan', 'ice cream');
susan.name;  // returns 'Susan'
susan.ask();  // returns 'Would you like to get some ice cream?'

We create our function and our methods object similarly to how you would if using functional-shared instantiation. The only difference is in the creation of the obj constant. Unlike functional and functional-shared, attaching methods in this manner places them on the created object's prototype, instead of in the object. This allows any object that inherits from our ProtoPerson object to have the same methods available to it! Handy! But aren't you tired of typing 'const obj = whatever' and 'return obj' all the time? If so, pseudoclassical is the way to go!

Pseudoclassical

Pseudoclassical instantiation handles method inheritance almost identically to the prototypal pattern, we just have a few syntactical differences. Lets look:

// Pseudoclassical instantiation
const PseudoPerson = function(name, favFood) {
  this.name = name;
  this.favFood = favFood;
};

PseudoPerson.prototype.greet = function () {
  return `Hi, my name is ${this.name}!`;
};

PseudoPerson.prototype.ask = function () {
  return `Would you like to get some ${this.favFood}?`;
};

const chuck = new PseudoPerson('Chuck', 'smores');
chuck.name;  // returns 'Chuck'
chuck.ask();  // returns 'Would you like to get some smores?'

As you can see, we now only have to type any desired properties into our function call. The use of the 'new' keyword when a new object is created handles initializing and returning that object. Additionally, we attach our methods directly to the object's prototype instead of using Object.create. There's one final pattern we should know about, ES6 Pseudoclassical.

ES6 Pseudoclassical

This pattern uses the class keyword to create your constructor function, then another constructor function inside of that handles assigning properties. Lets look at an example:

// ES6 Pseudoclassical instantiation
class ES6Person {
  constructor(name, favFood) {
    this.name = name;
    this.favFood = favFood;
  }

  greet() {
    return `Hi, my name is ${this.name}!`;
  };

  ask() {
    return `Would you like to get some ${this.favFood}?`;
  }
};

const becky = new ES6Person('Becky', 'waffles');
becky.name;  // returns 'Becky'
becky.ask();  // returns 'Would you like to get some waffles?'

We can see that class has replaced any function assignment, and our properties are attached inside of another nested function. Methods are once again created inside of the class function. This looks similar to the functional style, but it has all the inheritance patterns of pseudoclassical style! How convenient!

Top comments (0)