Self test, 7 interview questions about closure

Keywords: Javascript Interview

More articles are detailed in the official account [front end css and js dry cargo].

Every JavaScript developer must know what a closure is. In JavaScript coding interviews, closures are often asked.
This article has compiled a list of seven interesting and increasingly difficult questions about JavaScript closures.
Take a pencil and a piece of paper and try to answer the question without looking at the answer or running the code. It may take about 30 minutes.

1. Access external scope variables

Consider the following function, clickHandler,   immediate, and delayedReload:

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 the three functions accesses the external scope variable?

<details>
< summary > ▼ Click to view the answer < / summary >

  1. Countcopies of countcopies accesses variables from an external scope.
  2. immediate   Do not access any variables of the external scope.
  3. delayedReload   location accesses global variables from the global scope.
    </details>

2. Parameter missing

What does the following code print on the console?

(function immediateA(a) {

    return (function immediateB(b) {

        console.log(a); // What will be printed?

    })(1);

})(0);

<details>
< summary > ▼ Click to view the answer < / summary >
The console prints 0.

The function immediateA is called with parameter 0, so parameter a is 0.
The immediateB function nested in the immediateA function is a closure that captures a variable a from the external immediateA scope. So console.log(a) prints 0.
</details>

3. Who's who

What does the following code print on the console?

let count = 0;

(function immediate() {

    if (count === 0) {

        let count = 1;

        console.log(count); // Print what?

    }

    console.log(count); // Print what?

})();

<details>
< summary > ▼ Click to view the answer < / summary >
The console prints 1 and 0.

The first statement let count = 0 declares a variable count.
Immediate () is a closure that captures the count variable from an external scope. Inside the immediate() function, the scope count variable is 0.

However, in the if statement, another let count = 1 declares a local variable count, which overrides the count outside the scope. So the first console.log(count) records 1.

The second console.log(count) prints 0 because the count variable here is accessed from an external scope.
</details>

4. Tricky closures

What does the following code print on the console?

for (var i = 0; i < 3; i++) {
    setTimeout(function log() {

    console.log(i); // What will be printed?

    }, 1000);
}

<details>
< summary > ▼ Click to view the answer < / summary >
3,   3,3 will be printed to the console.

The code snippet is executed in two phases.

Phase 1

for() iterates 3 times. During each iteration, a new function log () is created that captures the variable i. setTimout() schedules log() to execute in 1000 milliseconds.
When the for() loop completes, the value of the i variable is 3.
Phase 2

The second phase occurs after 1000 milliseconds:

setTimeout() executes the log() function in the queue. Log() reads the current value of variable I, i.e. 3, and prints it to console 3.
That's why 3, 3, 3 are printed to the console.

Collateral challenge: how do I fix this example to print 0, 1, 2 values after 1 second?
</details>

5. Correct or wrong message

What does the following code print on the console?

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 will be printed?

<details>
< summary > ▼ Click to view the answer < / summary >
'Count is 0' is printed to the console.
The increment() function has been called 3 times, increasing the count to a value of 3.

The message variable within the scope of the createIncrement() function. Its initial value is' Count is 0 '. However, even if the count variable has been incremented several times, the message variable always remains' Count is 0 '.

The log() function is a closure that captures the message variable from the createIncrement() scope. console.log(message) prints "Count is 0" to the console.

</details>

Collateral challenge: how to fix the log() function to return the value of the message variable with the actual Count?

6. Restore packaging

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]; // Destroyed the package!

The stack worked as expected, but there was a small problem. Anyone can directly modify the items array because the stack.items property is public.

This is a problem because it breaks the encapsulation of the stack: only push() and pop() methods should be public, and stack.items or any other details should not be accessed externally.

Refactor the above stack implementation with closures, so that the array items cannot be accessed outside the scope of the function createStack():

function createStack() {

// Write your code here...

}

const stack = createStack();

stack.push(10);

stack.push(5);

stack.pop(); // => 5

stack.items; // => undefined

<details>
< summary > ▼ Click to view the answer < / summary >

This is a reconstructed 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 moved out of the returned object.

Because of this change, the items array cannot be accessed or modified from outside the scope of createStack(). Items is now a private variable and the stack is encapsulated: only push() and pop() methods are public.

The push() and pop() methods, as closures, capture the variable items from the scope of the createStack() function.

</details>

7. Intelligent multiplication

Write a multiply() function that multiplies two numbers:

function multiply(num1, num2) {

// Write your code here...

}

If multiply(num1, numb2) is called with two parameters, it should return the product of the two parameters.
However, if called with one parameter, the function should return another function const otherfunc = multiply (num1). The function otherfunc (num2) returned when called performs multiplication num1 * num2.

multiply(4, 5); // => 20

multiply(3, 3); // => 9

const double = multiply(2);

double(5); // => 10

double(11); // => 22

<details>
< summary > ▼ Click to view the answer < / summary >

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 number2 parameter is not undefined, the function simply returns number1 * number2.

However, if number2 is undefined, it means that the multiply() function is called as a parameter. In this case, we return a function doMultiply() and execute the actual multiplication when it is called later.

doMultiply() is a closure because it captures the variable number1 from the scope of multiply().
</details>

summary

Compare your answers to those in the post:

  • If you answer 5 or more questions correctly, you have a good understanding of closures
  • However, if you answered fewer than 5 questions correctly, you need to review closures.

Author: Dmitri Pavlutin translator: front end css and js dry goods source: Dmitri Pavlutin

Posted by john6384 on Mon, 29 Nov 2021 18:48:44 -0800