JavaScript

Factory Functions and the Module Pattern

JavaScript Course

Introduction

We have discussed object constructors in the previous lesson. However, they are one of the many ways to organize your code. While they are fairly common and a fundamental building block of the JavaScript language, they have their flaws.

Lesson overview

This section contains a general overview of topics that you will learn in this lesson.

  • Describe the scope of a variable.
  • Explore what closures are.
  • Briefly consider the disadvantages of using constructors.
  • Discuss Factory functions with examples.
  • Discuss Private variables and functions concerning factory functions.
  • Showcase object inheritance with the help of factory functions.
  • Describe what module pattern and IIFEs are.
  • Discuss encapsulation and how the module pattern helps with namespacing.

Scoopfuls of scopes

The word “scoping” essentially asks, “Where is a certain variable available to me?” - it indicates the current context of a variable. When a variable is not declared within any functions, existing outside any { curly braces }, they are said to be in the global scope, meaning that they are available everywhere. If they are within a function or { curly braces }, they are known to be locally scoped.

Before ECMAScript 6, JavaScript had a single keyword to declare a variable, var. These variables can be redefined and updated, and are said to be defined within the function scope, meaning, they are only available within the function they are declared in.

In ECMAScript 6, the keywords let and const were introduced. While var variables were function scoped, these allow you to define variables that are block scoped - basically, scoping the variable to only be available within the closest set of { curly braces } in which it was defined. These braces can be those of a for loop, if-else condition, or any other similar construct, and are called, a block. Let’s see an example to sum this all up.

let globalAge = 23; // This is a global variable

// This is a function - and hey, a curly brace indicating a block
function printAge (age) {
  var varAge = 34; // This is a function scoped variable

  // This is yet another curly brace, and thus a block
  if (age > 0) {
    // This is a block-scoped variable that exists
    // within its nearest enclosing block, the if's block
    const constAge = age * 2;
    console.log(constAge);
  }

  // ERROR! We tried to access a block scoped variable
  // not within its scope
  console.log(constAge);
}

printAge(globalAge);

// ERROR! We tried to access a function scoped variable
// outside the function it's defined in
console.log(varAge);

Take a while to brew on that example. In the end, it’s not some mind-blowing concept but there’s a whole bunch of terms in there - it’ll all help us understand the next mammoth - closures.

Closures aren’t scary

The best way to approach this would be to start with an example - take a look at this piece of code below.

function makeAdding (firstNumber) {
  // "first" is scoped within the makeAdding function
  const first = firstNumber;
  return function resulting (secondNumber) {
    // "second" is scoped within the resulting function
    const second = secondNumber;
    return first + second;
  }
}
// but we've not seen an example of a "function"
// being returned, thus far - how do we use it?

const add5 = makeAdding(5);
console.log(add5(2)) // logs 7

A lot going on, so let’s break it down:

  1. The makeAdding function takes an argument, firstNumber, declares a constant first with the value of firstNumber, and returns another function.
  2. When an argument is passed to the returned function, which we have assigned to add5, it returns the result of adding up the number passed earlier to the number passed now (first to second).

Now, while it may sound good at first glance, you may already be raising your eyebrows at the second statement. As we’ve learned, the first variable is scoped within the makeAdding function. When we declare and use add5, however, we’re outside the makeAdding function. How does the first variable still exist, ready to be added when we pass an argument to the add5 function? This is where we encounter the concept of closures.

Functions in JavaScript form closures. A closure refers to the combination of a function and the surrounding state in which the function was declared. This surrounding state, also called its lexical environment, consists of any local variables that were in scope at the time the closure was made. Here, add5 is a reference to the resulting function, created when the makeAdding function is executed, thus it has access to the lexical environment of the resulting function, which contains the first variable, making it available for use.

This is a crucial behavior of functions - allowing us to associate data with functions and manipulate that data anywhere outside of the enclosing function. If you’re still confused, read the MDN documentation on Closures, but only the sections “Lexical scoping”, “Closure” and “Practical closures”. The other sections refer to concepts that will be discussed later in this lesson.

So, what’s wrong with constructors?

One of the key arguments against constructors, in fact, the biggest argument is how they look like regular JavaScript functions, even though they do not behave like regular functions. If you try to use a constructor function without the new keyword, not only does your program fail to work, but it also produces error messages that are hard to track down and understand.

Yet another issue stems from the way the instanceof works. It checks the presence of a constructor’s prototype in an object’s entire prototype chain - which does nothing to confirm if an object was made with that constructor since the constructor’s prototype can even be reassigned after the creation of an object.

While still seen in code, these problems led to the use of a pattern that was similar to constructors but addressed a ton of these problems: Factory Functions.

Factory functions 🏭

These fancy-sounding functions work very similar to how constructors did, but with one key difference - they levy the power of closures. Instead of using the new keyword to create an object, factory functions set up and return the new object when you call the function. They do not use the prototype, which incurs a performance penalty - but as a general rule, this penalty isn’t significant unless you’re creating thousands of objects. Let’s take a basic example to compare them to constructor functions.

const User = function (name) {
  this.name = name;
  this.discordName = "@" + name;
}
// hey, this is a constructor - 
// then this can be refactored into a factory!

function createUser (name) {
  const discordName = "@" + name;
  return { name, discordName };
}
// and that's very similar, except since it's just a function,
// we don't need a new keyword

The object shorthand notation

Some may get confused by the way the returned object is written in the factory function example. In 2015, a shortcut to creating objects was added to JavaScript. Say we wanted to create an object with a name, age, and color, we would write it as follows:

const name = "Bob";
const age = 28;
const color = "red";

const thatObject = { name: name, age: age, color: color };

However, now, if we have a variable with the same name as that of the property to which we are assigning it, then we can write it once!

const nowFancyObject = { name, age, color };

An added advantage to this is that it’s now possible to console.log values neatly!

// If you wanted to log these values, earlier,
// you would have done the following
console.log(name, age, color);
// which would have resulted in a mess - Bob 28 red

// Try wrapping it in some { curly braces } now,
// which makes it an object!
console.log({ name, age, color });
// now it logs as - { name: "Bob", age: 28, color: "red" }

Destructuring

Yet another expression allows you to “unpack” or “extract” values from an object (or array). This is known as destructuring. When you have an object, you can extract a property of an object into a variable of the same name, or any named variable for an array. Take a look at the example below:

const obj = { a: 1, b: 2 };
const { a, b } = obj;
// This creates two variables, a and b,
// which are equivalent to
// const a = obj.a;
// const b = obj.b;

const array = [1, 2, 3, 4, 5];
const [ zerothEle, firstEle ] = array;
// This creates zerothEle and firstEle, both of which point
// to the elements in the 0th and 1st indices of the array

The MDN documentation on destructuring assignment has some great examples and should be a good read for this concept.

Private variables and functions

Now you may be thinking - where does closure come into all of this? Factories seem to be returning an object. This is where we can extend our User factory to add a few more variables and introduce “private” ones. Take a look at this, now:

function createUser (name) {
  const discordName = "@" + name;

  let reputation = 0;
  const getReputation = () => reputation;
  const giveReputation = () => reputation++;

  return { name, discordName, getReputation, giveReputation };
}

const josh = createUser("josh");
josh.giveReputation();
josh.giveReputation();

console.log({
  discordName: josh.discordName,
  reputation: josh.getReputation()
});
// logs { discordName: "@josh", reputation: 2 }

We’ve introduced a new metric for a new user - a reputation. Notice that the object we return in the factory function does not contain the reputation variable itself, nor any copy of its value. Instead, the returned object contains two functions - one that reads the value of the reputation variable, and another that increases its value by one. The reputation variable is what we call a “private” variable, since we cannot access the variable directly in the object instance - it can only be accessed via the closures we defined.

Concerning factory functions, a private variable or function uses closures to create smaller, dedicated variables and functions within a factory function itself - things that we do not need to return in the object itself. This way we can create neater code, without polluting the returned object with unnecessary variables that we create while creating the object itself. Often, you do not need every single function within a factory to be returned with the object, or expose an internal variable. You can use them privately since the property of closures allows you to do so.

In this case, we did not need control of the reputation variable itself. To avoid foot guns, like accidentally setting the reputation to -18000, we expose the necessary details in the form of getReputation and giveReputation.

Prototypal inheritance with factories

In the lesson with constructors, we looked deeply into the concept of prototype and inheritance, and how to give our objects access to the properties of another. With factory functions too, there are easy ways to do that. Take another hypothetical scenario into consideration. We need to extend the User factory into a Player factory that needs to control some more metrics - there are some ways to do that:

function createPlayer (name, level) {
  const { getReputation, giveReputation } = createUser(name);

  const increaseLevel = () => level++;
  return { name, getReputation, giveReputation, increaseLevel };
}

And there you go! You can create your User, extract what you need from it, and re-return whatever you want to - hiding the rest as some private variables or functions! In case you want to extend it, you can also use the Object.assign method to add on the properties you want!

function createPlayer (name, level) {
  const user = createUser(name);

  const increaseLevel = () => level++;
  return Object.assign({}, user, { increaseLevel });
}

The module pattern: IIFEs

ECMAScript 6 introduced a new JavaScript feature called “modules” - which are a set of syntax for importing and exporting code between different JavaScript files. While they are important and powerful, they are covered a bit later in the curriculum. We are not talking about them in this section.

Oftentimes, you do not need a factory to produce multiple objects - instead, you are using it to wrap sections of code together, hiding the variables and functions that you do not need elsewhere as private. This is easily achievable by wrapping your factory function in parentheses and immediately calling (invoking) it. This immediate function call is commonly referred to as an Immediately Invoked Function Expression (duh) or IIFE in short. This pattern of wrapping a factory function inside an IIFE is called the module pattern.

const calculator = (function () {
  const add = (a, b) => a + b;
  const sub = (a, b) => a - b;
  const mul = (a, b) => a * b;
  const div = (a, b) => a / b;
  return { add, sub, mul, div };
})();

calculator.add(3,5); // 8
calculator.sub(6,2); // 4
calculator.mul(14,5534); // 77476

In this example, we have a factory function creating some basic operations that we need only once. We can wrap it in parentheses and immediately call it by adding () - returning the result object that we store in calculator. In this way we can write code, wrapping away things that we do not need as private variables and functions inside our factory function and while they are tucked inside of our module, we can use the returned variables and functions outside the factory, as necessary.

Encapsulating with the module pattern

At first glance, this does not seem particularly useful. If we have some code that we use only once, why not write it in the main section of our JavaScript file itself? After all, the power of factory functions lies in being, well, a factory to make multiple objects, right?

This is where we encounter the word encapsulation - bundling data, code, or something into a single unit, with selective access to the things inside that unit itself. While it sounds general, this is what happens when we wrap, or encapsulate our code into modules - we don’t expose everything to the body of our program itself. This encapsulation leads to an effect called namespacing. Namespacing is a technique that is used to avoid naming collisions in our programs.

Take the calculator example into consideration. It’s very easy to imagine a scenario where you can accidentally create multiple functions with the name add. What does add do - does it add two numbers? Strings? Does it take its input directly from the DOM and display the result? What would you name the functions that do these things? Instead, we can easily encapsulate them inside a module called calculator which generates an object with that name, allowing us to explicitly call calculator.add(a, b) or calculator.sub(a, b).

Assignment

  1. WesBos has a beautiful and in-depth section on scopes and closures. Please check out these sections under “Module 3 - The Tricky Bits”:
  2. Read this article on module pattern in JavaScript by Tomek Buszewski.
  3. As an optional alternative, in case you prefer video lessons, this YouTube series on module pattern covers most of the content that we have discussed. Note that the videos include jQuery, but you don’t need to understand the jQuery syntax since the focus is on the module pattern concept.

Knowledge check

The following questions are an opportunity to reflect on key topics in this lesson. If you can’t answer a question, click on it to review the material, but keep in mind you are not expected to memorize or master this knowledge.

Additional resources

This section contains helpful links to related content. It isn’t required, so consider it supplemental.

Support us!

The Odin Project is funded by the community. Join us in empowering learners around the globe by supporting The Odin Project!