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

  1. clickHandler appeals to variable countClicks from the outer scope.

  2. immediate does not apply to any variables from the outer scope.

  3. delayReload appeals to a global variable location 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 0so the parameter is 0

Function immediateBbeing nested in a function immediateA, is a closure that captures the variable a from the outer scope immediateAwhere 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 0since 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

  1. for() iterates 3 times. During each iteration, a new function is created log()which captures the variable isetTimout() plans execution log() in 1000ms.

  2. When the cycle for() terminates, variable i matters 3.

Phase 2

The second phase occurs after 1000 ms:

  1. setTimeout() performs planned functions log()log() reads present value variable iwhich 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

Watch the demo

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

Watch the demo

If the parameter number2 is not undefinedthen 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

Similar Posts

Leave a Reply

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