2 min read

Closures

A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives you access to an outer function's scope from an inner function. In JavaScript, closures are created every time a function is created, at function creation time.

1. Lexical Scoping

To understand closures, you first need to understand lexical scoping. Lexical scoping means that the scope of a variable is defined by its position in the source code. Nested functions have access to variables declared in their outer scope.

function init() {
  var name = "Mozilla"; // name is a local variable created by init
  function displayName() {
    // displayName() is the inner function, a closure
    console.log(name); // use variable declared in the parent function
  }
  displayName();
}
init();

2. How Closures Work

Consider this example:

function makeFunc() {
  const name = "Mozilla";
  function displayName() {
    console.log(name);
  }
  return displayName;
}

const myFunc = makeFunc();
myFunc();

In some languages, local variables within a function exist only for the duration of that function's execution. Once makeFunc() finishes executing, you might expect the name variable to be no longer accessible. However, because the code still works, this is obviously not the case in JavaScript.

The reason is that myFunc has become a closure. A closure is a special kind of object that combines two things: a function, and the environment in which that function was created. The environment consists of any local variables that were in-scope at the time the closure was created.

3. Practical Use Cases

Data Privacy / Emulating Private Methods

JavaScript (prior to classes with private fields) didn't have a native way to declare private methods, but closures can emulate them.

const counter = (function () {
  let privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }

  return {
    increment() {
      changeBy(1);
    },
    decrement() {
      changeBy(-1);
    },
    value() {
      return privateCounter;
    }
  };
})();

console.log(counter.value()); // 0.
counter.increment();
counter.increment();
console.log(counter.value()); // 2.
counter.decrement();
console.log(counter.value()); // 1.

Function Factories

You can use closures to create functions that add a specific value to their argument.

function makeAdder(x) {
  return function (y) {
    return x + y;
  };
}

const add5 = makeAdder(5);
const add10 = makeAdder(10);

console.log(add5(2));  // 7
console.log(add10(2)); // 12

4. Common Pitfalls: Creating Closures in Loops

Prior to ES6 let keyword, a common mistake occurred when creating closures inside loops using var. The variable was shared across all closures. The solution is to use let (block scope) or an IIFE to create a new scope for each iteration.

programming/javascript/vanilla/javascript programming/functions-and-scope