DEV Community

ShangjieZhou
ShangjieZhou

Posted on

JS: Closure

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();
Enter fullscreen mode Exit fullscreen mode

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();
}
Enter fullscreen mode Exit fullscreen mode

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.
Enter fullscreen mode Exit fullscreen mode

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;
    }
}
Enter fullscreen mode Exit fullscreen mode

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);
  }
}
Enter fullscreen mode Exit fullscreen mode

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);
    };
  }
Enter fullscreen mode Exit fullscreen mode

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);
  }
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)