JavaScript
Article

Quick Tip: What Are Factory Functions in JavaScript

By Dan Prince

This article was peer reviewed by Jeff Mott. Thanks to all of SitePoint’s peer reviewers for making SitePoint content the best it can be!

You can’t get far as a JavaScript programmer without learning about functions and objects, and when used together, they are the building blocks we need to get started with a powerful object paradigm called composition. Today we’ll look at some idiomatic patterns for using factory functions to compose functions, objects and promises.

When a function returns an object, we call it a factory function.

Let’s take a look at a simple example.

function createJelly() {
  return {
    type: 'jelly',
    colour: 'red'
    scoops: 3
  };
}

Each time we call this factory, it will return a new instance of the jelly object.

It’s important to note that we don’t have to prefix our factory names with create but it can make the intent of the function clearer to others. The same is true with the type property but often it can help us differentiate between the objects flowing through our programs.

Parameterized Factory Functions

Like all functions, we can define our factory with parameters which change the shape of the returned object.

function createIceCream(flavour='Vanilla') {
  return {
    type: 'icecream',
    scoops: 3,
    flavour
  }
}

In theory, you could use parameterized factories with hundreds of arguments to return very specific and deeply nested objects, but as we’ll see, that’s not at all in the spirit of composition.

Composable Factory Functions

Defining one factory in terms of another helps us break complex factories into smaller, reusable fragments.

For example, we can create a dessert factory which is defined in terms of the jelly and ice cream factories from before.

function createDessert() {
  return {
    type: 'dessert',
    bowl: [
      createJelly(),
      createIceCream()
    ]
  };
}

We can compose factories to build arbitrarily complex objects that don’t require us to mess around with new or this.

Objects that can be expressed in terms of has-a relationships, rather than is-a can be implemented with composition, instead of inheritance.

For example, with inheritance.

// A trifle *is a* dessert

function Trifle() {
  Dessert.apply(this, arguments);
}

Trifle.prototype = Dessert.prototype;

// or

class Trifle extends Dessert {
  constructor() {
    super();
  }
}

We can express the same idea with composition.

// A trifle *has* layers of jelly, custard and cream. It also *has a* topping.

function createTrifle() {
  return {
    type: 'trifle',
    layers: [
      createJelly(),
      createCustard(),
      createCream()
    ],
    topping: createAlmonds()
  };
}

Async Factory Functions

Not all factories will be ready to return data immediately. For instance, some will have to fetch data first.

In these cases, we can define factories that return promises instead.

function getMeal(menuUrl) {
  return new Promise((resolve, reject) => {
    fetch(menuUrl)
      .then(result => {
        resolve({
          type: 'meal',
          courses: result.json()
        });
      })
      .catch(reject);
  });
}

This kind of deeply nested indentation can make asynchronous factories difficult to read and test. It can often be helpful to break them down into multiple distinct factories, then compose them.

function getMeal(menuUrl) {
  return fetch(menuUrl)
    .then(result => result.json())
    .then(json => createMeal(json));
}

function createMeal(courses=[]) {
  return {
    type: 'meal',
    courses
  };
}

Of course we could have used callbacks instead, but we already have tools like Promise.all for composing factories that return promises.

function getWeeksMeals() {
  const menuUrl = 'jsfood.com/';

  return Promise.all([
    getMeal(`${menuUrl}/monday`),
    getMeal(`${menuUrl}/tuesday`),
    getMeal(`${menuUrl}/wednesday`),
    getMeal(`${menuUrl}/thursday`),
    getMeal(`${menuUrl}/friday`)
  ]);
}

We’re using get rather than create as a naming convention to show that these factories do some asynchronous work and return promises.

Functions & Methods

So far, we haven’t seen any factories that return objects with methods and this is deliberate. This is because generally, we don’t need to.

Factories allow us to separate our data from our computations.

This means we’ll always be able to serialize our objects as JSON, which is important for persisting them between sessions, sending them over HTTP or WebSockets, and putting them into data stores.

For example, rather than defining an eat method on the jelly objects, we can just define a new function which takes an object as a parameter and returns a modified version.

function eatJelly(jelly) {
  if(jelly.scoops > 0) {
    jelly.scoops -= 1;
  }
  return jelly;
}

A little bit of syntactic help makes this a viable pattern for those who prefer to program without mutating data structures.

function eat(jelly) {
  if(jelly.scoops > 0) {
    return { ...jelly, scoops: jelly.scoops - 1 };
  } else {
    return jelly;
  }
}

Now, rather than writing:

import { createJelly } from './jelly';

createJelly().eat();

We’ll write:

import { createJelly, eatJelly } from './jelly';

eatJelly(createJelly());

The end result is a function which takes an object and returns an object.

And what do we call a function that return an object? A factory!

Higher Order Factories

Passing factories around as higher order functions gives us a huge amount of control. For example, we can use this concept to create enhancers.

function giveTimestamp(factory) {
  return (...args) => {
    const instance = factory(...args);
    const time = Date.now();
    return { time, instance };
  };
}

const createOrder = giveTimestamp(function(ingredients) {
  return {
    type: 'order',
    ingredients
  };
});

This enhancer takes an existing factory and wraps it to create a factory which returns instances with timestamps.

Alternatively, if we want to ensure that a factory returns immutable objects, we could enhance it with a freezer.

function freezer(factory) {
  return (...args) => Object.freeze(factory(...args)));
}

const createImmutableIceCream = freezer(createIceCream);

createImmutableIceCream('strawberry').flavour = 'mint'; // Error!

Conclusion

As a wise programmer once said:

It’s much easier to recover from no abstraction than the wrong abstraction.

JavaScript projects have a tendency to become hard to test and refactor because of the intricate layers of abstraction that we are often encouraged to build with.

Prototypes and classes implement a simple idea with complex and unnatural tools like new and this which still cause all kinds of confusion even now—years after they were added to the language.

Objects and functions make sense to programmers from most backgrounds and both are primitive types in JavaScript, so it could be argued that factories aren’t an abstraction at all!

Using these simple building blocks makes our code much friendlier for inexperienced programmers and that is definitely something we should all care about. Factories encourage us to model complex and asynchronous data with primitives that have a natural capacity for composition, without forcing us to reach for high level abstractions either. JavaScript is sweeter when we stick with simplicity!

  • James Edward Lewis

    Something to note is that in the phrase “a new instance of the jelly object”, the word “instance” does not mean the same thing as in OOP, where the instances share a prototype with some special properties, which is often the .prototype property of the function that created them; the objects created by this factory function, by contrast, share no link in the prototype chain other than Object.prototype.

    • nnnnnn321

      I thought the same thing. I gather within this paradigm the objects aren’t supposed to need to share a prototype, although if you did need that the factory function could just use new internally, or could use Object.assign(Object.create(somePrototype), { /* instance properties here /* }).

  • Rob Pocklington

    Great read. Really simple examples, framework agnostic, just raw Javascript and a good design pattern! These can really help when you build different fixtures or different types of similar objects.

  • Jeff Hansen

    I’m prefering factory functions over classes these days. When using dependency injection you won’t have to assign deps to `this` – you just use them as they were passed in as arguments.

  • Kenneth Davila

    Mmmmm…. I don’t know, unless you are really writing a lot of configurable code, this paradigm should be specified in that context. I think the title of this post is misleading, because you should specify and elaborate on the factory pattern and in which context for an application it makes sense. It seems you are trying to justify using this style in your everyday programming which I would not recommend. In module pattern, you return objects as well, those aren’t factories.

    You should read this and get up to speed on patterns in general.

    https://addyosmani.com/resources/essentialjsdesignpatterns/book/#factorypatternjavascript

    Scroll down to the part in that chapter about when not to use the factory pattern.

    A wise programmer should have a tool kit and knowledge of design patterns and WHEN to apply them.

    • Dan Prince

      Not sure the condescension was necessary but I’m well aware of the concept of design patterns and I read the Gang of Four book long before Addy was writing about them in the context of JavaScript.

      His article describes the factory pattern, not factory functions. That pattern involves instantiating a class which can then be used to create object instances. Factory functions bypass an entire layer of complexity, because you don’t have to write a class to start using them. Neither of the pieces of feedback in the “When Not To Use” section apply to factory functions.

      I absolutely am trying to justify using this style of programming everyday, because the implementation for prototypes is a broken system above a good idea. Factories have far fewer gotchas for new programmers than the elaborate set of rules around `this`.

      A wise programmer _should_ have a tool kit, and factory functions are one of the most useful tools they can add to it.

      • Kenneth Davila

        Definitely wasn’t trying to be condescending, this is an open forum so I would say be open to any criticism. I had a colleague read this post and he agreed that this seems to copy a builder pattern in general, and that this post lacks clarity and specifics. In any case, I disagree with this being a programming style as it’s clear you’re using a pattern, if the purpose of this is to help ‘new programmers’ .. then well I guess this is junior level concepts.

Recommended
Sponsors
Get the latest in JavaScript, once a week, for free.