Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Closures

A function in JavaScript remembers the variables of the place where it was created — even after that place has finished running. Such a function together with the variables it remembers is called a closure. Counters, settings and many library techniques are built on closures.

A function inside a function

A function can be declared inside another function and returned with return, like any value:

function makeGreeter() {
  return () => 'Hello!';
}
 
const greet = makeGreeter();
 
console.log(greet()); // Hello!

makeGreeter() returns a function; we store it in greet and call that.

The function remembers outer variables

An inner function sees the variables of the outer one — we know that from the scope lesson. The surprising part is that it still sees them after the outer function has finished:

function makeGreeter(name) {
  return () => `Hello, ${name}!`;
}
 
const greetAnna = makeGreeter('Anna');
 
console.log(greetAnna()); // Hello, Anna!

The call makeGreeter('Anna') ended long ago, but the returned function still remembers name. That is a closure.

A counter

A closure can not only read a remembered variable but also change it. This gives a counter that keeps its state between calls:

function makeCounter() {
  let count = 0;
  return () => {
    count++;
    return count;
  };
}
 
const counter = makeCounter();
 
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3

The variable count lives as long as the counter function exists.

Each call has its own environment

Every call of the outer function creates a new set of variables. That is why two counters do not interfere with each other:

function makeCounter() {
  let count = 0;
  return () => ++count;
}
 
const a = makeCounter();
const b = makeCounter();
 
a();
a();
 
console.log(a()); // 3
console.log(b()); // 1

A function factory

A function that creates and returns other functions is called a factory. It removes repetition when you need several similar functions with different settings:

function makeMultiplier(factor) {
  return (n) => n * factor;
}
 
const double = makeMultiplier(2);
const triple = makeMultiplier(3);
 
console.log(double(5)); // 10
console.log(triple(5)); // 15

Hidden data

A variable inside a closure cannot be reached from outside — only through the returned function. This protects data from accidental changes: in the counter example nobody can set count to a negative number; it can only be increased.

Common mistake

Every call of makeCounter() creates a new counter starting at zero. If you call the outer function every time, the count does not add up:

function makeCounter() {
  let count = 0;
  return () => ++count;
}
 
console.log(makeCounter()()); // 1
console.log(makeCounter()()); // 1

Create the counter once, store it in a variable and call that: const counter = makeCounter();.

Thanks for reading!

Donate to the site — we work hard for you.