7 questions about JavaScript closures
Hello, Habr. For future students of the course “JavaScript Developer. Professional” prepared a translation of interesting material.
As part of the recruitment of students for the course, we invite you to take part in open career webinar, where the teacher will tell about himself, his professional experience and answer questions about a career in this field.

7 Interview Questions on JavaScript Closures. Can you answer them?
Every JavaScript developer should know what a closure is. During a JavaScript coding interview, there is a good chance that you will be asked about the concept of a closure.
I have compiled a list of 7 interesting and difficult questions about JavaScript closures.
Take a pencil, a piece of paper and try to answer the questions without looking at the answers or running the code. According to my calculations, you will need about 30 minutes.
Have fun!
If you need to brush up on your knowledge of closure, I recommend checking out the post A Simple Explanation of JavaScript Closures (Simple explanation of JavaScript closures).
Content
Question 1: Closures untie your hands
Question 2: Lost parameters
Question 3: Who is who
Question 4: Clever closure
Question 5: Correct or incorrect message
Question 6: Restore encapsulation
Question 7: Smart multiplication
Summary
Question 1: Closures untie your hands
Consider the following functions: clickHandler
, immediate
, and delayReload
:
let countClicks = 0;
button.addEventListener('click', function clickHandler() {
countClicks++;
});
const result = (function immediate(number) {
const message = `number is: ${number}`;
return message;
})(100);
setTimeout(function delayedReload() {
location.reload();
}, 1000);
Which of these 3 functions access the outer scope variables?
Extended answer
clickHandler
appeals to variablecountClicks
from the outer scope.immediate
does not apply to any variables from the outer scope.delayReload
appeals to a global variablelocation
from the global scope (also known as the outermost scope).
Question 2: Lost parameters
What will be written to the console for the following code snippet:
(function immediateA(a) {
return (function immediateB(b) {
console.log(a); // What is logged?
})(1);
})(0);
Extended answer:
0
is written to the console. Watch the demo…
immediateA
called with an argument 0
so the parameter is 0
…
Function immediateB
being nested in a function immediateA
, is a closure that captures the variable a from the outer scope immediateA
where a
equally 0
… Thus, console.log(a)
writes to the log 0
…
Questions 3: Who’s Who
What will be written to the console for the following code snippet:
let count = 0;
(function immediate() {
if (count === 0) {
let count = 1;
console.log(count); // What is logged?
}
console.log(count); // What is logged?
})();
Extended answer:
1
and 0
are written to the console. Watch the demo…
First statement let count = 0
declares a variable count
…
immediate()
Is a closure that captures the count variable from the outer scope. Inside function scope immediate() count
is equal to 0
…
However, inside this condition, another command let count = 1
announces count
a local variable that overwrites count from the outer scope. The first console.log(count)
writes down 1
…
The second console.log(count)
writes down 0
since here the count variable is accessible from the outer scope.
Question 4: Clever closure
What will be written to the console in the following code snippet:
for (var i = 0; i < 3; i++) {
setTimeout(function log() {
console.log(i); // What is logged?
}, 1000);
}
Extended answer:
3
, 3
, 3
written to the console. Watch the demo…
The code snippet is executed in 2 steps.
Phase 1
for()
iterates 3 times. During each iteration, a new function is createdlog()
which captures the variablei
…setTimout()
plans executionlog()
in 1000ms.When the cycle
for()
terminates, variablei
matters 3.
Phase 2
The second phase occurs after 1000 ms:
setTimeout()
performs planned functionslog()
…log()
reads present value variablei
which is 3 and writes to the console 3.
therefore 3
, 3
, 3
is written to the console.
Additional challenge: how would you modify the console message with values in this example 0, one, 2 ? Write down your solution in the comments below!
Question 5: Correct or incorrect message
What will be written to the console in the following code snippet:
function createIncrement() {
let count = 0;
function increment() {
count++;
}
let message = `Count is ${count}`;
function log() {
console.log(message);
}
return [increment, log];
}
const [increment, log] = createIncrement();
increment();
increment();
increment();
log(); // What is logged?
Extended answer:
'Count is 0'
is written to the console. Watch the demo…
Function increment()
was called 3 times, eventually increasing count
to a value of 3.
Variable message
exists within the function createIncrement()
… Its initial value 'Count is 0'
… However, even if the variable count
has been increased several times, the variable message
always matters 'Count is 0'
…
Функция log()
Is a closure that grabs the message variable from scope createIncrement()
… console.log(message)
writes down 'Count is 0'
to the console.
Additional challenge: how would you fix the function log ()so that it returns a message that has the actual value count? Write down your solution in the comments below!
Question 6: Restore encapsulation
Next function createStack()
creates elements of the stack data structure:
function createStack() {
return {
items: [],
push(item) {
this.items.push(item);
},
pop() {
return this.items.pop();
}
};
}
const stack = createStack();
stack.push(10);
stack.push(5);
stack.pop(); // => 5
stack.items; // => [10]
stack.items = [10, 100, 1000]; // Encapsulation broken!
The stack works as expected, but with one small problem. Anyone can modify the array of elements directly, because the property stack.items
open.
This is an annoying detail as it breaks stack encapsulation: only methods push()
and pop()
should be public and stack.items
or any other elements should not be available.
Refactor the stack implementation described above using the concept of a closure so that it is not possible to access the array items
out of scope createStack()
:
function createStack() {
// Write your code here...
}
const stack = createStack();
stack.push(10);
stack.push(5);
stack.pop(); // => 5
stack.items; // => undefined
Extended answer:
Here is a possible refactoring of a function createStack()
:
function createStack() {
const items = [];
return {
push(item) {
items.push(item);
},
pop() {
return items.pop();
}
};
}
const stack = createStack();
stack.push(10);
stack.push(5);
stack.pop(); // => 5
stack.items; // => undefined
items
was moved to a variable inside the scope createStack()
…
Thanks to this change, out of scope createStack()
, now there is no way to access the array items
or change it. Elements are now private and the stack is encapsulated: only methods push()
and pop()
are public.
Methods push()
and pop()
, being closed, capture the variable items
from function scope createStack()
…
Question 7: Smart multiplication
Write a function multiply()
which multiplies 2 numbers:
function multiply(num1, num2) {
// Write your code here...
}
If a multiply(num1, numb2)
will be called with 2 arguments, then it should return the multiplication of 2 arguments.
But in the event that 1 argument is called const anotherFunc = multiply(numb1)
, then the function must return another function. Returned function when called anotherFunc(num2)
performs multiplication num1 * num2
…
multiply(4, 5); // => 20
multiply(3, 3); // => 9
const double = multiply(2);
double(5); // => 10
double(11); // => 22
Extended answer:
Here is a possible implementation of the function multiply()
:
function multiply(number1, number2) {
if (number2 !== undefined) {
return number1 * number2;
}
return function doMultiply(number2) {
return number1 * number2;
};
}
multiply(4, 5); // => 20
multiply(3, 3); // => 9
const double = multiply(2);
double(5); // => 10
double(11); // => 22
If the parameter number2
is not undefined
then the function simply returns number1*number2
…
But if number2
is an undefined
, then this means that the function multiply()
was called with one argument. In this case, return the function doMultiply()
which, on a subsequent call, performs the actual multiplication.
doMultiply()
is a closure because it captures the variable number1
from function scope multiply()
…
Summary
Compare your answers to the answers in the article:
If you answered 5 or more questions correctly, you have a good understanding of closures.
If you answered less than 5 questions correctly, you need to brush up on the topic of snapping a bit. I recommend to study my post. A Simple Explanation of JavaScript Closures (Simple explanation of closures in JavaScript).
Are you ready for a new challenge? Try to answer 7 interview questions for the “this” keyword in JavaScript…
Learn more about the course “JavaScript Developer. Professional”…
Watch open career webinar…