JS: Closure

RMAG news

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);
}
}

Leave a Reply

Your email address will not be published. Required fields are marked *