Skip to content
On this page

Closure

Functions as Return Values

Higher-order functions can not only accept functions as parameters but can also return functions as results.

Let's implement a function to sum an array. Typically, a sum function is defined like this:

javascript
function sum(arr) {
    return arr.reduce(function (x, y) {
        return x + y;
    });
}

sum([1, 2, 3, 4, 5]); // 15

However, if we don't want to calculate the sum immediately but instead want to compute it later based on our needs, we can return a function that performs the summation!

javascript
function lazy_sum(arr) {
    let sum = function () {
        return arr.reduce(function (x, y) {
            return x + y;
        });
    }
    return sum;
}

When we call lazy_sum(), what is returned is not the sum result but the sum function:

javascript
let f = lazy_sum([1, 2, 3, 4, 5]); // function sum()

Only when we call the function f do we actually compute the sum:

javascript
f(); // 15

In this example, we defined the function sum within lazy_sum, and the inner function sum can reference the parameters and local variables of the outer function lazy_sum. When lazy_sum returns the function sum, the relevant parameters and variables are preserved in the returned function. This programming structure, known as "closure," has immense power.

Note that each time we call lazy_sum(), a new function is returned, even if the same parameters are passed:

javascript
let f1 = lazy_sum([1, 2, 3, 4, 5]);
let f2 = lazy_sum([1, 2, 3, 4, 5]);
f1 === f2; // false

The calls to f1() and f2() do not affect each other.

Closure

Notice that the returned function references the local variable arr in its definition. Therefore, when a function returns another function, the internal local variables are still referenced by the new function. While closures are easy to use, they can be complex to implement.

Another important point is that the returned function does not execute immediately; it only executes when f() is called. Let's look at an example:

javascript
function count() {
    let arr = [];
    for (var i = 1; i <= 3; i++) {
        arr.push(function () {
            return i * i;
        });
    }
    return arr;
}

let results = count();
let [f1, f2, f3] = results;

In the above example, each loop creates a new function, and those three functions are added to an array and returned.

You might think that calling f1(), f2(), and f3() should return 1, 4, and 9, but the actual results are:

javascript
f1(); // 16
f2(); // 16
f3(); // 16

They all return 16! The reason is that the returned functions reference the variable i, which was defined using var, but it does not execute immediately. By the time the three functions are returned, the referenced variable i has become 4, resulting in the final output being 16.

One takeaway when returning closures is to avoid referencing any loop variables or variables that may change later.

What if you need to reference a loop variable? The solution is to create another function that binds the current value of the loop variable as a parameter. Regardless of how the loop variable changes afterward, the bound value remains unchanged:

javascript
function count() {
    let arr = [];
    for (var i = 1; i <= 3; i++) {
        arr.push((function (n) {
            return function () {
                return n * n;
            }
        })(i));
    }
    return arr;
}

let [f1, f2, f3] = count();

f1(); // 1
f2(); // 4
f3(); // 9

Note that here we used a "create and immediately execute an anonymous function" syntax:

javascript
(function (x) {
    return x * x;
})(3); // 9

Theoretically, creating and immediately executing an anonymous function could be written as:

javascript
function (x) { return x * x }(3);

However, due to JavaScript syntax parsing issues, it will throw a SyntaxError. Therefore, the entire function definition needs to be enclosed in parentheses:

javascript
(function (x) { return x * x })(3);

Typically, an immediately executed anonymous function can break down the function body and is often written as:

javascript
(function (x) {
    return x * x;
})(3);

Another approach is to define the loop variable i using let within the for loop body, as let scoping ensures a new i is bound in each iteration:

javascript
function count() {
    let arr = [];
    for (let i = 1; i <= 3; i++) {
        arr.push(function () {
            return i * i;
        });
    }
    return arr;
}

However, if i is defined outside of the for loop, it remains incorrect:

javascript
function count() {
    let arr = [];
    let i;
    for (i = 1; i <= 3; i++) {
        arr.push(function () {
            return i * i;
        });
    }
    return arr;
}

Therefore, the best approach is still to avoid referencing any loop variables in returned functions.

After all this, is closure just for returning a function and delaying execution?

Certainly not! Closures have very powerful functionalities. For example:

In object-oriented programming languages like Java and C++, private variables can be encapsulated within an object using the private modifier.

In languages that lack class mechanisms and only use functions, closures can similarly encapsulate private variables. Here's how we can create a counter in JavaScript:

javascript
function create_counter(initial) {
    let x = initial || 0;
    return {
        inc: function () {
            x += 1;
            return x;
        }
    }
}

It can be used like this:

javascript
let c1 = create_counter();
c1.inc(); // 1
c1.inc(); // 2
c1.inc(); // 3

let c2 = create_counter(10);
c2.inc(); // 11
c2.inc(); // 12
c2.inc(); // 13

In the returned object, a closure is implemented that carries the local variable x, which is inaccessible from external code. In other words, a closure is a function that carries state, and its state can be completely hidden from the outside.

Closures can also transform functions with multiple parameters into functions with a single parameter. For example, to compute (x^y) we can use the Math.pow(x, y) function. However, considering the frequent need to calculate (x^2) or (x^3), we can use closures to create new functions pow2 and pow3:

javascript
function make_pow(n) {
    return function (x) {
        return Math.pow(x, n);
    }
}

// Create two new functions:
let pow2 = make_pow(2);
let pow3 = make_pow(3);

console.log(pow2(5)); // 25
console.log(pow3(7)); // 343
Closure has loaded