To organize code in a standard way in a program, we have design patterns to enable us to do this.
The book Design Patterns: Elements of Reusable Object-Oriented Software was published in 1994 which came up with 23 design patterns that are used by object-oriented programs.
In this article, we’ll look at some of the more commonly used design patterns in JavaScript programs, including the singleton, iterator and factory patterns.
Singleton
Singleton is a pattern that is common in JavaScript. It’s a class that only creates one instance of an object only.
In JavaScript, we have the object literal to define an object that isn’t an instance of a class. For example, we have:
const obj = {
foo: 1
}
We also have the class syntax, which does the same thing as constructor functions, where we can define a getInstance
method to get an instance of a class.
To make a singleton class, we can write something like the following:
class Foo {
static getInstance() {
this.foo = 1;
this.instance = this;
return this.instance;
}
}
const foo1 = Foo.getInstance();
const foo2 = Foo.getInstance();
console.log(foo1 === foo2);
Since we assigned this
to this.instance
and returns it in getInstance
, we should always get the same reference.
We should get that foo1 === foo2
being true
since they reference the same instance of the Foo
class.
Singleton classes are useful for facade objects to hide the complexities of a program. State objects which are shared by different parts of a program also make singleton classes a good choice.
They also let us share data without creating global variables. Global scope isn’t polluted, so it’s a good choice for sharing data.
Iterator
The iterator pattern is a pattern where we create an iterator to sequentially access data in a container.
We need this pattern to traverse items in collections without exposing the underlying data structure. And we should also be able to traverse objects in an aggregate object without changing its interface.
In JavaScript, we can define iterable objects and generators to do this.
To define an iterable object, we can write:
const iterableObj = {
*[Symbol.iterator]() {
let index = 0;
const arr = [1, 2, 3];
while (index < arr.length) {
yield arr[index];
index++
}
}
}
for (const obj of iterableObj) {
console.log(obj);
}
As we can see iterableObj
hides the array that’s inside it from the outside. Also, we can change it to anything else we want and not have to worry about changing the code outside.
[Symbol.iterator]
and generator functions have been available since ES6. Ever since then, we can define iterators easily.
We can also define generator functions which return generators. To do this, we can write the following:
const generatorFn = function*() {
let index = 0;
const arr = [1, 2, 3];
while (index < arr.length) {
yield arr[index];
index++
}
}
for (const obj of generatorFn()) {
console.log(obj);
}
It’s similar to iterableObj
since they both use generators. The difference is that generatorFn
is a generator function, which returns generators. Generators are what we iterate through.
Factory
The factory pattern centers around the factory function. It’s a function that returns objects without using the new
keyword to construct an instance of a class or a constructor function.
We want to use the factory pattern to make code more readable since it lets us create functions that returns new objects from more code.
It also lets us return objects without knowing the code that creates the object. We don’t have to worry about what class is instantiated to create the object or how it’s created otherwise.
In JavaScript, when a function returns an object without the new
keyword, then it’s a factory function.
For example, we can create a simple factory function as follows:
const createFoo = () => ({
foo: 1
});
The createFoo
function always returns the { foo: 1 }
object when it’s called. It always returns an object without the new
keyword, so it’s a factory function and uses the factory pattern.
Factory functions are common in JavaScript. For example, browsers have the document.querySelector()
method to get a DOM object given the CSS selector.
There’re similar methods that return objects everywhere in JavaScript.
The singleton pattern creates a single instance of an object. In JavaScript, we can do this with object literals or a getInstance
method of a class that always returns the same instance of a class.
It’s useful for sharing data and hiding the complexity of implementation.
The iterator pattern lets us traverse through collections of objects without knowing the implementation of it. Also, it lets us change the underlying data structure and logic without changing the interface.
JavaScript has iterators to do this, in addition to generator functions.
Finally, the factory pattern is common in JavaScript. Anything function that returns a new object without using the new
keyword to instantiate it is a factory function.
It’s useful for hiding the complexities of creating objects from other developers. For example, we don’t have to worry about how document.querySelector()
gets an element from the DOM. It just returns the first DOM element that matches the selector.