Мы рассмотрим самые хитрые и, возможно, наиболее часто задаваемые вопросы о закрытии.

Начнем с основ.

Замыкание - это комбинация объединенной функции (заключенной) со ссылками на ее окружающее состояние (лексическое окружение). Другими словами, замыкание дает вам доступ к области внешней функции из внутренней функции. В JavaScript замыкания создаются каждый раз, когда создается функция, во время создания функции.

- Веб-документы MDN

Проще говоря, Closure - это просто функция, вложенная в функцию, которая имеет доступ к состоянию своей родительской функции.

function myFunc() {
  var data = 'MyFunc'      // Local varialbe of myFunc
  function displayData {   // Well this function is a closure
    alert(data);           // Access variable declared in parent 
  }
  return displayData;
}

var func = myFunc();
func();

Что ж, теперь, когда у нас это есть, давайте погрузимся в возможные вопросы, которые вам могут задать во время интервью.

1. Напишите функцию, которая будет увеличивать число на 1 (или любое другое число).

Давай подумаем об этом. Нам нужна функция, которая при задании числа будет увеличивать его на предопределенную базу. Решение требует, чтобы мы создали замыкание, которое запоминает базовое значение из своей родительской функции и возвращает результат, добавляя текущее и базовое значения.

function myFunc(base) {
    return function(num){
        console.log(num + base)
    }
}
var fun=myFunc(1)
fun(10)// logs 11
fun(20)// logs 21
var fun=myFunc(2)
fun(10)// logs 12
fun(20)// logs 22

2. Что дает данный код?

var num = 10;
(()=>{
   console.log(num);
   var num = 20;
   console.log(num);
})();

Возможны следующие варианты:

  • 10 20
  • undefined undefined
  • 20 20
  • undefined 20

Правильный ответ здесь: undefined 20. Это связано с объемом самого закрытия и небольшой функцией Javascript, называемой подъемом. Когда приведенный выше код интерпретируется, он преобразуется в этот

var num = 10;
(()=>{
   var num;
   console.log(num);   // undefined
   num = 20;
   console.log(num);   // 20
})();

Javascript берет замедление переменной num и перемещает его в верхнюю часть закрытия (но не на глобальный уровень). Пока оставил задание там, где было. Таким образом, первый console.log печатает undefined, а второй печатает значение 20.

3. Создайте личный счетчик.

После того, как вы разберетесь с двумя приведенными выше вопросами, это должно быть проще простого. Посмотрите код ниже:

function myCounter() {
    let counter = 0
    const myFunction = function() {
        counter = counter + 1
        return counter
    }
    return myFunction
}
var increment = myCounter()
increment() // returns 1
increment() // returns 2
increment() // returns 3

Мы создаем функцию myCounter, которая имеет одну частную переменную counter. Эта функция myCounter вместе со своей внутренней функцией образует закрытие, которое инкапсулирует данные и их логику. Следовательно, когда мы объявляем новое приращение переменной и присваиваем ему определение функции возвращаемой функции, при вызове приращения выполняется внутренняя функция, добавляется 1 к переменной счетчика и возвращается ее.

4. Умножьте 3 числа, например, mul (2) (3) (4).

Ну, кто-то новичок в JS может подумать, что такое (2) (3) (4)? 😕 Но это простой пример каррирования.

Давайте сначала разберемся в проблеме. Нам нужна функция, которая принимает число и возвращает нам другую функцию, которая, в свою очередь, также возвращает нам функцию, которая умножает входные данные. Еще не запутались?

Что ж, не надо просто смотреть ниже.

function mul(a) {
    return function(b) { // closure
        return function(c) { // closure
            return a * b * c;
        };
    };
}
mul(2)(3)(4)

Функция mul () принимает a как первый аргумент и возвращает нам функцию, которая является замыканием. Эта вторая функция принимает аргумент b и возвращает нам третью функцию, которая также является закрытием. И, наконец, третья функция принимает аргумент c, умножает все аргументы и возвращает нам результаты.

5. Что дает данный код?

Сохранил лучшее напоследок. (Вероятно, наиболее часто задаваемый)

var i;
for (i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log(i);  
  }, 100);
}

Если вы только начали готовиться к JS-собеседованию, то, вероятно, ваш ответ будет 0,1,2. Но нет, это неправильно (к сведению - я тоже не понял с первого раза 😅 ).

Правильный ответ: 3 печатается 3 раза. Ладно, пошагово. Сначала цикл for выполняется 3 раза, и для каждой итерации создается новая анонимная функция, которая присоединяется к установленному таймауту. Все 3 итерации происходят очень быстро, прежде чем пройдут 100 мсек, и даже первая установленная функция тайм-аута может быть выполнена.

Все закрытия получают ссылку на i. По прошествии 100 мсек и пора запускать внутренние функции значение i становится равным 3. Следовательно, вывод 3 печатается 3 раза.

Если вам понравилось это читать, не забывайте аплодисменты. 👏
Спасибо.