Javascript, classes VS factory functions

In Javascript we have lot of freedom when it comes to object creation compared to a language like Java, where you have to use classes for object creation. We can use the Object Constructor, function Constructor and a few other methods. But today we are going to look at two ways to create objects, namely, es6/ES2015 classes and factory functions.

Table of contents

  • Classes
  • Factory functions
  • When to use what
  • My personal take on it
  • conclusion

Classes

Classes were added as syntactical sugar in ES2015 for Javascript’s prototype system. It does not provide a new way to create objects, but it improves readability and offers simple syntax to deal with with inheritance. let me illustrate with an example.

es5 classes syntax

// Mamal - superclass
function Mamal(species) {
this.species = species;
this.x = 0;
this.y = 0;
}

// Superclass method
Mamal.prototype.move = function(x, y) {
this.x += x;
this.y += y;
}

// Human - subclass
function Human(height, weight) {
// Call constructor of superclass to initialize superclass-derived members.
Mamal.call(this, "Human");

// Initialize subclass's own members
this.height = height;
this.weight = weight;
}

// Human derives from Mamal
Human.prototype = Object.create(Mamal.prototype);
Human.prototype.constructor = Human;

// Subclass methods. Add them after Human.prototype is created with
// Object.create
Human.prototype.sleep = function() {
console.log("Sleeping...");
}

This code is way more convoluted than it needs to be, and takes some time to interpret. You have to manually set the prototype of the Human class to get the methods of the parent class, Mamal. Bellow it is written in ES2015 class syntax.

es2015 class syntax

class Mamal {
constructor(species) {
this.species = species;
this.x = 0;
this.y = 0;
}
move(x, y) {
this.x += x;
this.y += y;
}
}
class Human extends Mamal {
constructor(height, weight) {
// call Mamal constructor
super("Human");
this.height = height;
this.weight = weight;
}
sleep() {
console.log("Sleeping...");
}
}

As you can see the ES2015 syntax is a lot more expressive than the ES5 syntax. So that is the one we are going to stick with.


Factory functions

Factory functions are very easy to understand as long as you are familiar with closures. You can read more about them here. The definition is Closures are functions that refer to independent (free) variables (variables that are used locally, but defined in an enclosing scope). In other words, these functions ‘remember’ the environment in which they were created.” Here is a simple example using factory functions.

const dog = () => {
const sound = "Woof!";
return {
bark: () => {
console.log(sound);
}
};
}
let flipper = dog();

If you call flipper.bark(); you will get the expected “Woof!”. All the dog function is doing is returning an object literal consisting of a method, bark, in this case the bark function is a closure and has access to the local sound variable.


When to use what

It might be difficult to decide when to use what method of object creation. It is mostly a matter of personal preference and also performance. Creating objects using the class method is actually faster than factory functions, but when using Function.bind, which you have to use when passing a callback because the “this” context is not set automatically, the class method of object creation actually becomes slower than factory functions! There are workarounds for this, you can wrap your callback in a function, like so

element.addEventListener(‘click’, () => { flipper.bark() });

but it is unfortunate that you even have to remember to do this. When using a factory functions you could simply type this:

element.addEventListener(‘click’, flipper.bark);

And it would work with no problems! Factory functions can also mimic a lot of the functionality of classes like private variables, if you want to make them public, simply set it as a property on the object that is being returned. What factory functions can not mimic however is the syntactical sugar of ES2015 classes, you could still use the prototype to achieve inheritance, but it gets exponentially more difficult to digest your code the more of it you have, Rather use ES2015 classes if you are building an ecosystem that is going to have a lot of classes that are inheriting from other classes.


Personal take

For most cases i believe that factory functions are the way to go, it will make your code simpler and easier to reason about, you will also avoid the pitfalls of having to using Function.bind when assigning a function that is a member of a class to a variable, or passing it as a callback.


Conclusion

I hope you enjoyed this overview of classes VS factory functions. It can be difficult to know what is correct to use in different cases, especially since JavaScript is the polar opposite of languages such as Java and other C derivatives when it comes to creating objects, since it is so few rules.

Maximilian Øystå Lloyd

Written by

Compulsive hustler

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade