Menu

Vojtěch Korduliak

Programmer and Photographer

Facebook / LinkedIn / Instagram

© Designed and created by Vojtěch Korduliak

Closures, Currying, and Composition in JavaScript

12.05.2025 15:46
Blog

Today's topic explores three powerful functional programming concepts in JavaScript: closures, function composition, and currying. Understanding these mechanisms, especially how currying and composition utilizes closures, might potentialy improve the readability and structure of your code.

Closures

A closure is a fundamental concept where a function remembers and continues to access variables from its enclosing (outer) scope. It effectively "closes over" the environment in which it was defined. In some ways, closures can provide an alternative to certain object-oriented patterns, like encapsulating state.

function someClosureFunction() {
	let contextVariable = 'Some value from the outer scope';

	return function () {
		// This inner function has access to 'contextVariable'
		console.log(contextVariable);
	};
}

// Call the outer function, which returns the inner function
let innerFunction = someClosureFunction();

// Execute the inner function later
innerFunction(); // Outputs: "Some value from the outer scope"

The innerFunction retains access to variables defined in someClosureFunction's scope, such as contextVariable. This allows you to create functions with a pre-configured context or private-like state.

However, be mindful of memory implications. Since innerFunction holds a reference to the scope of someClosureFunction, the garbage collector cannot free the memory occupied by contextVariable as long as innerFunction is still reachable and potentially callable. If references to closures are held longer than necessary, it can lead to increased memory consumption, sometimes referred to as a memory leak if unintentional.

Composition

Function composition is the process of combining multiple functions to create a new function, where the output of one function becomes the input for the next. It promotes building complex operations from smaller, reusable units.

Here's a typical way to chain operations:

function add2(a) {
	return a + 2;
}

function multiplyBy3(a) {
	return a * 3;
}

// First multiply 2*3 = 6, then add 2 => 8
let result = add2(multiplyBy3(2)); // returns 8
console.log(result);

And here's how you can achieve the same result using composition:

// A helper function to compose two functions (f after g)
function compose(f, g) {
	return function (a) {
		return f(g(a));
	};
}

// Create a composed function
let addAndMultiply = compose(add2, multiplyBy3);

// Use the composed function
let composedResult = addAndMultiply(2); // returns 8 (applies multiplyBy3 first, then add2)
console.log(composedResult);

Currying

Currying is a technique, utilizing closures, that transforms a function accepting multiple arguments into a sequence of nested functions, each accepting only a single argument. This process enables partial application, where you can supply some arguments now and get back a new function that waits for the remaining arguments. It helps avoid repeatedly passing the same arguments.

Here's a standard function with multiple arguments:

function add(a, b, c) {
	return a + b + c;
}

add(2, 3, 1); // returns 6

And here's the curried version:

function addCurried(a) {
	return function (b) {
		return function (c) {
			return a + b + c;
		};
	};
}

// Using partial application:
let add2ToNumber = addCurried(2); // add2ToNumber is now a function: (b) => (c) => 2 + b + c
let add2And3ToNumber = add2ToNumber(3); // add2And3ToNumber is now a function: (c) => 2 + 3 + c

let finalResult = add2And3ToNumber(1); // returns 6 (calculates 2 + 3 + 1)
console.log(finalResult);

// Alternatively, chain the calls directly:
let chainedResult = addCurried(2)(3)(1); // also returns 6
console.log(chainedResult);