JS: Closure
Tags: javascript
Published: 2024-05-06 04:32:51

## 1. What is closure

A combination of a function and the environment the within which that function was declared. For example:
```
function makeFunc() {
  const name = "Mozilla";
  function displayName() {
    console.log(name);
  }
  return displayName;
}

const myFunc = makeFunc();
myFunc();
```
The closure here is the combination of function `displayName` and the variable `name`. Therefore, function `displayName` remains a reference to variable `name`, and when later `myFunc` is invoked, it'll be be able to access the variable `name` hence print out `Mozilla`.

**Closures in JavaScript capture variables by reference, not by value**, which means if the value of the lexical environemtn variable changes, it will be reflected inside the function. For example:
```
myFunctions = []

let i = 0;
while (i < 3) {
  const a = () => console.log(i);
  myFunctions.push(a);
  i += 1;
}

for (let f of myFunctions) {
  f();
}
```

## 2. Using closure for private methods

We can utilise JS closure to write **encapsulated functions and variables** that are inaccessable outside an object and to **expose functions and variables** to outer environments, like private and public methods in Java:

```
const makeCounter = function () {
  let privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    increment() {
      changeBy(1);
    },

    value() {
      return privateCounter;
    },
  };
};

const counter = makeCounter();
console.log(counter.value()); // 0.
counter.increment();
console.log(counter.value()); // 1.
```
which is equivalent to the Java code below:
```
public class Counter {
    private int privateCounter = 0;

    private void changeBy(int val) {
        privateCounter += val;
    }

    public void increment() {
        changeBy(1);
    }

    public int value() {
        return privateCounter;
    }
}
```

## 3. Closure within closure

The following code will not behave as expected:

```
function showHelp(help) {
  document.getElementById("help").textContent = help;
}

function setupHelp() {
  var helpText = [
    { id: "email", help: "Your email address" },
    { id: "name", help: "Your full name" },
    { id: "age", help: "Your age (you must be over 16)" },
  ];

  for (var i = 0; i < helpText.length; i++) {
    var item = helpText[i];
    document.getElementById(item.id).onfocus = () => showHelp(item.help);
  }
}
```
But changing the declaration of `item` from `var` to `const` will work, since now in every loop a new variable is created with the updated value:

```
for (let i = 0; i < helpText.length; i++) {
    const item = helpText[i];
    document.getElementById(item.id).onfocus = () => {
      showHelp(item.help);
    };
  }
```
Or we can keep the `var`, but using yet another closure to capture the variable's live value within each loop by creating another inner function:

```
function showHelp(help) {
  document.getElementById("help").textContent = help;
}

function makeHelpCallback(help) {
  return function () {
    showHelp(help);
  };
}

function setupHelp() {
  var helpText = [
    { id: "email", help: "Your email address" },
    { id: "name", help: "Your full name" },
    { id: "age", help: "Your age (you must be over 16)" },
  ];

  for (var i = 0; i < helpText.length; i++) {
    var item = helpText[i];
    document.getElementById(item.id).onfocus = makeHelpCallback(item.help);
  }
}
```