Skip to main content

Factory function

A factory function is a function that returns other functions. When called, it creates and hands back a new function whose behaviour depends on the arguments you passed in, which is exactly why it is called a factory.

Theory

TL;DR

  • A factory is a function whose result is another function.
  • It is built on closures: the produced function remembers the factory's arguments.
  • Every call to the factory yields an independent instance with its own state.
  • Main benefits: reuse of logic, data encapsulation, flexibility.
  • Typical examples: counters, filters, loggers, partial application.

Quick example

javascript
function createGreeter(greeting) { return function (name) { console.log(`${greeting}, ${name}!`); }; } const sayHi = createGreeter('Hello'); const sayBye = createGreeter('Goodbye'); sayHi('Maria'); // Hello, Maria! sayBye('Oleh'); // Goodbye, Oleh!

Here createGreeter is the factory, while sayHi and sayBye are the functions it "produced". Each of them closes over its own greeting value.

Private state through closures

The most valuable property of a factory: every call creates a separate variable environment that only the returned function can reach.

javascript
function createCounter(start = 0) { let count = start; return function () { count++; return count; }; } const counterA = createCounter(); const counterB = createCounter(10); console.log(counterA()); // 1 console.log(counterA()); // 2 console.log(counterB()); // 11

counterA and counterB do not interfere with each other: each has its own count, and there is no way to reach it from the outside except through the returned function. That is encapsulation without classes and without this.

Configuring behaviour: filters

A factory lets you push what varies into a parameter and stop duplicating the rest of the code.

javascript
function createFilter(minValue) { return function (numbers) { return numbers.filter(n => n >= minValue); }; } const filterAbove10 = createFilter(10); console.log(filterAbove10([5, 8, 13, 21])); // [13, 21]

Partial application

When a factory fixes part of a future call's arguments, you get partial application, a close relative of currying.

javascript
function multiplyBy(factor) { return function (number) { return number * factor; }; } const double = multiplyBy(2); const triple = multiplyBy(3); console.log(double(5)); // 10 console.log(triple(5)); // 15

It is a convenient way to build specialised functions on the fly and pass them to map, filter or event handlers.

A real case: a logger

javascript
function createLogger(prefix) { return function (message) { console.log(`[${prefix}] ${message}`); }; } const info = createLogger('INFO'); const error = createLogger('ERROR'); info('Server started'); // [INFO] Server started error('Something went wrong'); // [ERROR] Something went wrong

One template, different functions for different contexts.

Why it is convenient

BenefitDescription
Code reuseThe factory creates many similar functions without duplicating logic
Data encapsulationIt uses closures to hold internal state
FlexibilityYou can create functions with dynamic behaviour
Functional styleFits well with map, filter, reduce

Summary table

PropertyDescription
What it isA function that returns another function
Core idea"Generate" functions from a shared template
What it relies onClosures
Where it is usedConfiguration, handlers, loggers, counters, factory patterns
BenefitsFewer repetitions, more flexibility and readability

Common mistakes

  • Confusing a factory function with an object factory: the first returns a function, the second returns an object (though both rest on the same idea).
  • Moving the state outside the factory, for example into a module variable. Then every "produced" function shares one counter and the independence is gone.
  • Creating a factory inside a render or a loop for no reason: a brand new function is born every time, which means extra allocations and broken memoisation in UI frameworks.
  • Forgetting that a closure keeps a reference to everything it captured. If the factory captured a large object, it will not be freed while the returned function is alive.
  • Building three levels of nesting where one extra argument would do: the code gets harder to read with no benefit at all.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.