Русский
Русский
English
Статистика
Реклама

Перевод 7 вопросов про замыкания в JavaScript

Привет, Хабр. Для будущих студентов курса "JavaScript Developer. Professional" подготовили перевод интересного материала.

В рамках набора студентов на курс приглашаем принять участие в открытом карьерном вебинаре, где преподаватель расскажет о себе, своём профессиональном опыте и ответит на вопросы касательно карьеры в этой области.


7 Вопросов из интервью по замыканиям (closures) в JavaScript. Можете ли вы ответить на них?

Каждый разработчик JavaScript должен знать, что такое замыкание (closure). Во время собеседования по кодированию JavaScript есть большая вероятность, что вас спросят о концепции замыкания.

Я составил список из 7 интересных и наиболее сложных вопросов по замыканиям в JavaScript.

Возьмите карандаш, лист бумаги и постарайтесь ответить на вопросы, не глядя на ответы и не запуская код. По моим подсчетам, вам понадобится около 30 минут.

Развлекайтесь!

Если вам нужно освежить свои знания о замыкании, я рекомендую ознакомится с публикацией A Simple Explanation of JavaScript Closures (Простое объяснение замыканиям в JavaScript).

Содержание

Вопрос 1: Замыкания развязывают твои руки

Вопрос 2: Утерянные параметры

Вопрос 3: Кто есть кто

Вопрос 4: Хитроумное замыкание

Вопрос 5: Правильное или неправильное сообщение

Вопрос 6: Восстановление инкапсуляции (Restore encapsulation)

Вопрос 7: Умное перемножение

Резюме

Вопрос 1: Замыкания развязывают твои руки

Рассмотрим следующие функции: clickHandler, immediate, и 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);

Какие из этих 3 функций получают доступ к переменным внешней области видимости (outer scope)?

Расширенный ответ

  1. clickHandler обращается к переменной countClicks из внешней области видимости.

  2. immediate не обращается ни к каким переменным из внешней области видимости.

  3. delayReload обращается к глобальной переменной location из глобальной области видимости (так же известной как крайняя область видимости (outermost scope)).

Вопрос 2: Утерянные параметры

Что будет записано в консоль для следующего фрагмента кода (code snippet):

(function immediateA(a) {  return (function immediateB(b) {    console.log(a); // What is logged?  })(1);})(0);

Расширенный ответ:

0 записывается на консоль. Посмотрите демо.

immediateA вызывается с аргументом 0, таким образом, параметр равен 0.

Функция immediateB, будучи вложенной в функцию immediateA, является замыканием, которое захватывает переменную a из внешней области видимости immediateA, где a равно 0. Таким образом, console.log(a) записывает в журнал 0.

Вопросы 3: Кто есть кто

Что будет записано в консоль для следующего фрагмента кода (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?})();

Расширенный ответ:

1 и 0 записываются в консоль. Посмотрите демо.

Первое утверждение let count = 0 объявляет переменную count.

immediate() это замыкание, которое захватывает переменную count из внешней области видимости. Внутри области видимости функции immediate() count равна 0.

Однако внутри этого условия другая команда let count = 1 объявляет count локальной переменной, которая перезаписывает count из внешней области видимости. Первая console.log(count) записывает 1.

Вторая console.log(count) записывает 0, так как здесь переменная count доступна из внешней области видимости.

Вопрос 4: Хитроумное замыкание

Что будет записано в консоль в следующем фрагменте кода (code snippet):

for (var i = 0; i < 3; i++) {  setTimeout(function log() {    console.log(i); // What is logged?  }, 1000);}

Расширенный ответ:

3, 3, 3 записано на консоль. Посмотрите демо.

Фрагмент кода выполняется в 2 этапа.

Фаза 1

  1. for() выполняет итерацию 3 раза. Во время каждой итерации создается новая функция log(), которая захватывает переменную i. setTimout() планирует исполнение log() через 1000мс.

  2. Когда цикл for() завершается, переменная i имеет значение 3.

Фаза 2

Вторая фаза происходит после 1000 мс:

  1. setTimeout() выполняет запланированные функции log(). log() считывает текущее значение переменной i, которое равно 3, и записывает в консоль 3.

Поэтому 3, 3, 3 записывается в консоль.

Дополнительная задача: как бы вы изменили в этом примере сообщение для консоли со значениями 0, 1, 2 ? Запишите ваше решение в комментариях ниже!

Вопрос 5: Правильное или неправильное сообщение

Что будет записано в консоль в следующем фрагменте кода (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?

Расширенный ответ:

'Count is 0' записывается в консоль. Посмотрите демо.

Функция increment() вызывалась 3 раза, в итоге увеличивая count до значения 3.

Переменная message существует в рамках функции createIncrement(). Ее начальное значение 'Count is 0'. Однако, даже если переменная count была увеличена несколько раз, переменная message всегда имеет значение 'Count is 0'.

Функция log() это закрытие, которое захватывает переменную message из области видимости createIncrement(). console.log(message) записывает 'Count is 0' в консоль.

Дополнительная задача: как бы вы исправили функцию log(), чтобы она возвращала сообщение, имеющее фактическое значение count? Запишите ваше решение в комментариях ниже!

Вопрос 6: Восстановление инкапсуляции (Restore encapsulation)

Следующая функция createStack() создает элементы структуры стековых данных:

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(); // => 5stack.items; // => [10]stack.items = [10, 100, 1000]; // Encapsulation broken!

Стек работает, как и ожидалось, но с одной маленькой проблемой. Любой может изменить массив элементов напрямую, потому что свойство stack.items открыто.

Это неприятная деталь, так как она нарушает инкапсуляцию стека: только методы push() и pop() должны быть публичными, а stack.items или любые другие элементы не должны быть доступны.

Рефакторизуйте описанную выше реализацию стека, используя концепцию замыкания, так, чтобы не было возможности доступа к массиву items вне области видимости функции createStack():

function createStack() {  // Write your code here...}const stack = createStack();stack.push(10);stack.push(5);stack.pop(); // => 5stack.items; // => undefined

Расширенный ответ:

Вот возможный рефакторинг (refactoring) функции 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(); // => 5stack.items; // => undefined

Посмотрите демо.

items был перемещен в переменную внутри области видимости createStack().

Благодаря этому изменению, за пределами области видимости createStack(), теперь нет способа получить доступ к массиву itemsили изменить его. Элементы сейчас являются приватной переменной, а стек инкапсулирован: только методы push() и pop() являются публичными.

Методы push() и pop(), будучи замкнутыми, захватывают переменную items из области видимости функции createStack().

Вопрос 7: Умное перемножение

Напишите функцию multiply(), которая умножает 2 числа:

function multiply(num1, num2) {  // Write your code here...}

Если multiply(num1, numb2) будет вызвана с 2 аргументами, то она должна вернуть умножение 2 аргументов.

Но в том случае, если вызывается 1 аргумент const anotherFunc = multiply(numb1), то функция должна возвращать другую функцию. Возвращаемая функция при вызове anotherFunc(num2) выполняет умножение num1 * num2.

multiply(4, 5); // => 20multiply(3, 3); // => 9const double = multiply(2);double(5);  // => 10double(11); // => 22

Расширенный ответ:

Вот возможная имплементация функции multiply():

function multiply(number1, number2) {  if (number2 !== undefined) {    return number1 * number2;  }  return function doMultiply(number2) {    return number1 * number2;  };}multiply(4, 5); // => 20multiply(3, 3); // => 9const double = multiply(2);double(5);  // => 10double(11); // => 22

Посмотрите демо.

Если параметр number2 не является undefined, то функция просто возвращает number1*number2.

Но если number2 является undefined, то это означает, что функция multiply() была вызвана с одним аргументом. В таком случае вернем функцию doMultiply(), которая при последующем вызове выполняет фактическое умножение.

doMultiply() является замыкающей, поскольку она захватывает переменную number1 из области видимости функции multiply().

Резюме

Сравните ваши ответы с ответами в статье:

  • Если вы правильно ответили на 5 или более вопросов, у вас есть хорошее представление о замыканиях.

  • Если вы правильно ответили менее чем на 5 вопросов, вам нужно хорошенько освежить тему замыкания,. Я рекомендую изучить мой пост A Simple Explanation of JavaScript Closures (Простое объяснение замыкания в JavaScript).

Готовы к новому испытанию? Попробуйте ответить на 7 вопросов в интервью по ключевому слову "this" в JavaScript.


Узнать подробнее о курсе "JavaScript Developer. Professional".

Смотреть
открытый карьерный вебинар.

Источник: habr.com
К списку статей
Опубликовано: 15.03.2021 14:07:31
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Блог компании otus

Javascript

Программирование

Closure

Interview

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru