Basic JavaScript Design Patterns- Object Creation

John Au-Yeung - Jan 30 '21 - - Dev Community

Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62

Subscribe to my email list now at http://jauyeung.net/subscribe/

JavaScript lets us do a lot of things. It’s sometimes too forgiving in its syntax.

To organize our code, we should use some basic design patterns. In this article, we’ll look at some basic object creation patterns that we may use.

Singleton

The singleton pattern is where we create a single instance of a class.

Since we can create object literals without a constructor in JavaScript, we can create an object easily as follows:

const obj = {  
  foo: 'bar'  
};
Enter fullscreen mode Exit fullscreen mode

obj will also be a standalone object, so defining an object literal follows the singleton pattern.

If we create a new object that has the same structure, we’ll get a different object.

For instance, if we create a new object:

const obj2 = {  
  foo: 'bar'  
};
Enter fullscreen mode Exit fullscreen mode

Then when we write:

obj2 === obj
Enter fullscreen mode Exit fullscreen mode

to compare 2 objects, then that’ll return false since they’re different references.

Using new

We can also create a singleton object even if we create them from constructors or classes.

For instance, we can write:

let instance;  
class Foo {  
  constructor() {  
    if (!instance) {  
      instance = {  
        foo: 'bar'  
      };  
    }  
    return instance;  
  }  
}
Enter fullscreen mode Exit fullscreen mode

Then if we create 2 Foo instances:

const foo1 = new Foo();  
const foo2 = new Foo();
Enter fullscreen mode Exit fullscreen mode

Then when we compare them:

console.log(foo1 === foo2);
Enter fullscreen mode Exit fullscreen mode

We see true logged.

We check if the instance is created and then return it as is if it is. Otherwise, we set it to an object.

Since instance won’t change after it’s set, all instances are the same.

We can make the instance variable private by putting it in an IIFE.

For instance, we can write:

const Foo = (() => {  
  let instance;  
  return class {  
    constructor() {  
      if (!instance) {  
        instance = {  
          foo: 'bar'  
        };  
      }  
      return instance;  
    }  
  }  
})()
Enter fullscreen mode Exit fullscreen mode

Now we can only access the returned instance rather than looking at the constructor.

An Instance in a Static Property

Alternatively, we can put the instance of the singleton in a static property of the constructor or class.

For instance, we can write:

class Foo {  
  constructor() {  
    if (!Foo.instance) {  
      Foo.instance = {  
        foo: 'bar'  
      };  
    }  
    return Foo.instance;  
  }  
}
Enter fullscreen mode Exit fullscreen mode

This way, we set the public instance property of the class Foo to return the instance if it exists. Otherwise, it’ll create it first.

Now if we write:

const foo1 = new Foo();  
const foo2 = new Foo();

console.log(foo1 === foo2);
Enter fullscreen mode Exit fullscreen mode

We get the same result as before.

Instance in a Closure

We can also put the singleton instance in a closure.

For instance, we can write:

function Foo() {  
  const instance = this;  
  this.foo = 'bar';  
  Foo = function() {  
    return instance;  
  }  
}
Enter fullscreen mode Exit fullscreen mode

We can write a constructor function that gets reassigned to itself at the end of the function.

This will let us return the instance while using this to assigning instant variables.

This won’t work with the class syntax since it’s created as a constant, so we can’t reassign it to a new value.

Factory

We can create a factory function to create an object.

A factory function is just a regular function that we can use to create objects.

It’s useful for performing repeated operations when setting up similar objects.

It also offers a way for users of the factory to create objects without the specific class at compile time.

For instance, we can create our own factory function as follows:

class Animal {}  
class Dog extends Animal {}  
class Cat extends Animal {}  
class Bird extends Animal {}

const AnimalMaker = type => {  
  if (type === 'dog') {  
    return new Dog()  
  } else if (type === 'cat') {  
    return new Dog()  
  } else if (type === 'bird') {  
    return new Dog()  
  }  
}
Enter fullscreen mode Exit fullscreen mode

The code above has the AnimalMaker factory function.

It takes the type parameter which allows us to create different subclasses given the type .

So we can call it as follows:

const animal = AnimalMaker('dog');
Enter fullscreen mode Exit fullscreen mode

to create a Dog instance.

We can also add methods as we wish into the classes:

class Animal {  
  walk() {}  
}  
class Dog extends Animal {  
  bark() {}  
}  
class Cat extends Animal {}  
class Bird extends Animal {}

const AnimalMaker = type => {  
  if (type === 'dog') {  
    return new Dog()  
  } else if (type === 'cat') {  
    return new Dog()  
  } else if (type === 'bird') {  
    return new Dog()  
  }  
}
Enter fullscreen mode Exit fullscreen mode

This will also work.

Conclusion

We can create objects with a single instance y using object literals or checking if the instance of the class exists and create it if it doesn’t exist.

Factory functions are useful for creating similar objects.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .