Microblog: using closures to create JavaScript factories
By: Shailesh Vasandani
Tags: Frontend, Microblog, Webdev, JavaScript

JavaScript factories tend to be much less imposing.

Hi everyone! In today’s Microblog post, we’ll be looking at JavaScript closures and how you can use them to make factories.

First, though — why learn about this technique? Well, even though many people dive straight into frameworks like React and Angular, it’s always good to understand the fundamental vanilla JavaScript underlying those frameworks. As a result, you’ll be able to do more both with and without the frameworks supporting you. Now, onto closures:

What are closures?

Good question. At their core, closures are simply an enclosed scope inside a function. They allow an inner function to access the variables in an outer function. A super simple example would look something like this:

const addTo = (numberOne) => {

return (numberTwo) => {

return numberOne + numberTwo;

}

} const addToFive = addTo(5);

const addToTen = addTo(10); addtoFive(3); // => 8

addToTen(3); // => 13

When the addTo function is called with a parameter, it returns another function that has access to the numberOne variable; this returned function is the closure. Both addToFive and addToTen each have their own unique scope, where the variable numberOne equals 5 and 10 respectively. As a result, when calling those functions with 3 as a parameter, they give the expected results of 8 and 13. Now, onto factories:

What are factories?

Factories are generally used in testing to create objects without creating the full object declaration inline. A simple example might look this:

/* User {

{string} firstName

{string} lastName

{number} age

}

const createUser = (userObj) => {

// create mock user

let user = {

firstName: "John",

lastName: "Doe",

age: 21

}; Object.keys(user).forEach((userKey) => { }); return user;

}

This allows us to scope our testing to be more relevant to the tests we perform.

// WITHOUT FACTORY

const returnsFalseForMinors = () => {

// irrelevant data in test

let user = { firstName: "John", lastName: "Doe", age: 17 }); console.assert(functionToTest(user), false);

} // WITH FACTORY

const returnsFalseForMinors = () => {

let user = createUser({ age: 17 }); console.assert(functionToTest(user), false);

}

Factories and closures, together?

When we use factories together with closures, we’re able to dynamically generate useful functions that don’t have to take too many parameters. Here’s an example from the codebase for my photography page, where I needed to add and remove different classes for large amounts of objects:

// closure and factories, working together

const removeAndAdd = (remove, add) => {

return (el) => {

el.classList.remove(remove);

el.classList.add(add);

}

} // methods generated by the factory for use later

const leftToCenter = removeAndAdd("left", "center");

const centerToRight = removeAndAdd("center", "right");

const rightToCenter = removeAndAdd("right", "center");

const centerToLeft = removeAndAdd("center", "left"); // ... const movePrev = () => {

if (currentIndex <= 0) return;

else {

centerToRight(images[currentIndex]);

leftToCenter(images[--currentIndex]); // decrement inline

labelText.innerHTML = (currentIndex + 1) + "/" + numImages;

labelTitle.innerHTML = altText[currentIndex];

}

} const moveNext = () => {

if (currentIndex + 1 >= numImages) return;

else {

centerToLeft(images[currentIndex]);

rightToCenter(images[++currentIndex]); // increment inline

labelText.innerHTML = (currentIndex + 1) + "/" + numImages;

labelTitle.innerHTML = altText[currentIndex];

}

}

As you can see, by using a closure as a function factory, I was able to avoid repeating calls to each element’s classList , making my code more readable and semantic in the process.

I hope this short post gives you an idea of the power of closures in JavaScript, and I’m hoping to make a longer post further down the line detailing the most powerful ways these can be used. Make sure to follow me to be notified when that post drops.

If you found this post useful, please consider buying me a coffee. Until next time!