Какие вопросы об отладке часто задают на собеседовании JS-разработчикам: примеры и объяснения

В подборку попали типичные вопросы об отладке, с которыми сталкиваются на собеседованиях разработчики на JavaScript. Попытайтесь ответить на них самостоятельно, а потом сравните ответ с правильным решением.
Содержание
- Вопрос 1: объекты и ссылки на объекты
- Вопрос 2: область видимости и event loop
- Вопрос 3: контекст функции
- Вопрос 4: прототипы и ключевое слово
class
- Вопрос 5: строгое равенство
- Вместо заключения
Вопрос 1: объекты и ссылки на объекты
Почему вместо hey amy
получаем результат hey arnold
?
Ответ
Вот секрет: { name: 'amy' } !== { name: 'amy' }
. Когда JavaScript проверяет равенство или строгое равенство объектов, он использует ссылки на объект. В данном случае мы видим объекты с одинаковыми свойствами. Но для JavaScript это два разных объекта.
Чтобы получить ожидаемый результат, нужно использовать такой код:
Вопрос 2: область видимости и event loop
Почему код из примера ниже не выводит значения с 0 до 3 по порядку?
Проблема
Это неочевидный вопрос. Чтобы правильно ответить на него, нужно понимать область видимости и цикл событий (event loop) в JavaScript.
Проблема прячется в нулевой задержке. setTimeout(() => console.log(i), 0)
не значит, что колбэк выполнится через 0 миллисекунд. Посмотрим на код через призму event loop:
- Текущий стек вызовов установлен для первого
setTimeout()
. window.setTimeout()
рассматривается в качестве веб API для выполнения неблокирующих операций ввода-вывода (I/O). Стек вызовов отправляет этот код в веб API. Через 0 миллисекунд колбэк, в данном случае — анонимная функция, отправляется в очередь (queue), но не в стек вызовов.- Поскольку стек вызовов свободен, цикл for переходит ко второму
setTimeout()
и работает, пока верно условиеi < 4
. - Цикл завершается, когда
i === 4
. Теперь JavaScript выполняет очередь колбэков. Каждый console.log(i) выводит4
.
Ещё одна проблема связана с областью видимости. В примере выше четыре экземпляра setTimeout()
работают с одним экземпляром i
. Это хорошо иллюстрирует код из примера ниже.
Ответ
Задачу можно решить несколькими способами. Первый — используйте функцию-обёртку для setTimeout()
.
Второй способ — просто используйте ключевое слово let
вместо var
. Переменная, объявленная с помощью var
, сохраняется при каждом повторении цикла. А при использовании let
при каждом повторении цикла создаётся новая переменная.
Вопрос 3: контекст функции
Дан код:
Почему получаем результат undefined
?
Ответ
В коде выше определяется объект dog
, в котором два свойства. Мы копируем свойство объекта в константу sayName
, а затем вызываем sayName
в глобальном контексте. Функция sayName()
возвращает window.name
, так как в Node.js вызов происходит в глобальном контексте, где typeof window.name === 'undefined'
.
Проблему можно решить двумя способами: удобным и не очень. Сначала не очень удобный подход: привязываем sayName
к контексту dog
с помощью bind
.
Более удобный подход: сразу вызываем функцию в нужном контексте.
Вопрос 4: прототипы и ключевое слово class
Дан код:
Почему собака по кличке fido не лает?
Ответ
В выводе появляется ошибка: TypeError: fido.bark is not a function
. Есть несколько решений задачи. Вот не самый удобный подход:
fido.bark
— не функция, но у нас есть функция Dog.bark
. Поэтому в примере выше решаем задачу с помощью function.prototype.bind()
. Но использование function.prototype.bind()
часто приводит к проблемам, поэтому удобнее определить bark()
в прототипе Dog
:
И самый удобный вариант: использовать синтаксис ES2015, в частности, ключевое слово class
.
Вопрос 5: строгое равенство
Почему код из примера ниже ведёт себя не так, как мы ожидаем?
Ответ
В коде используется оператор сравнения ==
. Этот оператор позволяет сравнивать разные типы данных. Вот что происходит.
С аргументом 1
функция isBig()
работает как ожидается. При вызове isBig([2])
срабатывает условие thing == 2
. При сравнении массива и числа JavaScript преобразует массив в число. Так работает алгоритм сравнения абстрактного равенства: когда мы сравниваем число и объект, а массивы в JS — это объекты, объект преобразуется в число. В нашем массиве один элемент, поэтому [2] == 2
.
Неявные преобразования сложно отслеживать, поэтому при использовании оператора ==
возможны сюрпризы. Рекомендуется использовать оператор сравнения ===
.
Вместо заключения
У нас есть репозиторий с реальными тестовыми заданиями от разных компаний. Используйте его, чтобы оценить свой уровень и прокачаться перед собеседованиями. А если у вас остались вопросы по задачам для JavaScript-разработчиков, пишите в комментариях.
Адаптированный перевод статьи Typical JavaScript interview exercises (explained) by Maxence Poutord.
Дмитрий Дементий
6 лет назад