Перетяжка, Карта дня
Перетяжка, Карта дня
Перетяжка, Карта дня

15 вопросов с собеседований по фронтенду для мидлов

Пов: вы пришли на собес на мидла. В статье — ищите вопросы и пытайтесь ответить сначала сами. Если не получается — ответы и решения тоже есть.

3К открытий20К показов
15 вопросов с собеседований по фронтенду для мидлов

Статей в духе «вопросы для фронтенд разработчика написано много». Опыт Альберта Халимова в прохождении и проведении собеседований показывает, что, задавая базовые вопросы, интервьюер хочет узнать, готовился ли человек и насколько он заинтересован в работе.

На типичные вопросы Junior-разработчик должен легко ответить. Вместе с Альбертом Халимовым, TeamLead Frontend Developer в Группе М.Видео-Эльдорадо и ментором Эйч Навыки, разбираемся в тех же вопросах, но смотрим на них под другим углом и узнаем самое главное — готовы ли быть мидлом.

1. Что такое оператор typeof?

Вызов typeof x возвращает строку с именем типа:

			typeof undefined // "undefined"
typeof 0 // "number"
typeof 10n // "bigint"
typeof true // "boolean"
typeof "foo" // "string"
typeof Symbol("id") // "symbol"

		

2. Что вернет typeof null, typeof new Date, typeof alert?

			typeof null // "object"
typeof new Date // " object "
typeof alert // " function "
		

Date — это встроенный объект, который содержит дату и время, а также предоставляет методы управления ими.

Результатом вызова typeof null является "object". Это официально признанная ошибка в typeof, берущая начало еще со времен создания JavaScript и сохраненная для совместимости. Конечно, null — не объект. Это специальное значение с отдельным типом.

Вызов typeof alert возвращает "function", потому что alert — функция. Функции относятся к объектному типу. Но typeof обрабатывает их особым образом, возвращая "function". Так тоже повелось с момента создания JavaScript. Формально это неверно, но может быть удобным на практике.

Интересный факт: информация об операторе typeof — это последний пункт главы «Типы данных» на https://learn.javascript.ru/.

3. Что такое NaN? Какого типа это значение? Как можно узнать, равно ли значение переменной NaN?

NaN расшифровывается как "Not A Number", это "false" (ложное) значение. Будьте аккуратны: выражение typeof NaN возвращает тип Number. Чтобы проверить значение переменной на соответствие NaN, нужно воспользоваться встроенным методом isNaN() или оператором тройного равенства ===.

4. Какие есть способы объявления функций? В чем их отличия? Что такое стрелочная функция?

Ниже рассмотрим ключевые отличия Function Declaration от Function Expression.

Во-первых, синтаксис: Function Declaration — функция объявляется отдельной конструкцией function… в основном потоке кода.

			// Function Declaration
function sum(a, b) {
  return a + b;
}

		

Function Expression — функция, созданная внутри другого выражения или синтаксической конструкции. В данном случае функция создается в правой части «выражения присваивания» =:

			// Function Expression
let sum = function(a, b) {
  return a + b;
};

		

Более тонкое отличие появляется тогда, когда функция создаётся движком JavaScript. Так, Function Expression появляется в то время, как выполнение доходит до него, и затем уже может использоваться.

После того, как поток выполнения достигнет правой части выражения присваивания let sum = function…, функция считается созданной и может быть использована (присвоена переменной, вызвана и т.д. ).

С Function Declaration всё иначе. Она может быть вызвана раньше, чем объявлена. Другими словами, когда движок JavaScript готовится выполнять скрипт или блок кода, прежде всего, он ищет в нём Function Declaration и создаёт все такие функции. Можно считать этот процесс «стадией инициализации».

Только после того, как все объявления Function Declaration будут обработаны, продолжится выполнение. В результате функции, созданные как Function Declaration, могут быть вызваны раньше своих определений.

Например, будет работать так:

			sayHi("Вася"); // Привет, Вася
function sayHi(name) {
  alert( `Привет, ${name}` );
}

		

Функция sayHi была создана, когда движок JavaScript подготавливал скрипт к выполнению, и такая функция видна повсюду в этом скрипте.

Если бы это было Function Expression, то такой код вызвал бы ошибку:

			sayHi("Вася"); // ошибка!
let sayHi = function(name) {  // (*) магии больше нет
  alert( `Привет, ${name}` );
};

		

Функции, объявленные при помощи Function Expression, создаются тогда, когда выполнение доходит до них. Это случится только на строке, помеченной звёздочкой (*). Слишком поздно.

Ещё одна важная особенность Function Declaration заключается в её блочной области видимости.

В строгом режиме, когда Function Declaration находится в блоке {...}, функция доступна везде внутри блока, но не снаружи.

Для примера давайте представим, что нам нужно объявить функцию welcome() в зависимости от значения переменной age, которое мы получим во время выполнения кода. И затем запланируем использовать её когда-нибудь в будущем.

Если мы попробуем использовать Function Declaration, это не заработает так, как задумывалось:

			let age = prompt("Сколько Вам лет?", 18);
// в зависимости от условия объявляем функцию
if (age < 18) {
  function welcome() {
    alert("Привет!");
  }
} else {
  function welcome() {
    alert("Здравствуйте!");
  }
}
// ...не работает
welcome(); // Error: welcome is not defined

		

Это произошло, так как объявление Function Declaration видно только внутри блока кода, в котором располагается.

Вот ещё один пример:

			let age = 16; // возьмём для примера 16
if (age < 18) {
  welcome();               // \   (выполнится)
                           //  |
  function welcome() {     //  |
    alert("Привет!");      //  |  Function Declaration доступно
  }                        //  |  во всём блоке кода, в котором объявлено
                          //  |
  welcome();               // /   (выполнится)
} else {
  function welcome() {
    alert("Здравствуйте!");
  }
} // здесь фигурная скобка закрывается,
// поэтому Function Declaration, созданные внутри блока кода выше -- недоступны отсюда.
welcome(); // Ошибка: welcome is not defined

		

Что можно сделать, чтобы welcome была видна снаружи if? Верным подходом будет воспользоваться функцией, объявленной при помощи Function Expression, и присвоить значение welcome переменной, объявленной снаружи if, что обеспечит нам нужную видимость.

Такой код заработает, как ожидалось:

			let age = prompt("Сколько Вам лет?", 18);
let welcome;
if (age < 18) {
  welcome = function() {
    alert("Привет!");
  };
} else {
  welcome = function() {
    alert("Здравствуйте!");
  };
}
welcome(); // теперь всё в порядке

		

Или мы могли бы упростить это ещё сильнее, используя условный оператор:

			let age = prompt("Сколько Вам лет?", 18);
let welcome = (age < 18) ?
  function() { alert("Привет!"); } :
  function() { alert("Здравствуйте!"); };
welcome(); // теперь всё в порядке

		

Так когда использовать Function Declaration, а когда Function Expression?

Как правило, если нам понадобилась функция, нужно рассматривать синтаксис Function Declaration, который мы использовали до этого. Он даёт больше свободы в том, как мы можем организовывать код. Функции, объявленные таким образом, можно вызывать до их объявления.

Также функции вида function f(…) {…} чуть более заметны в коде, чем let f = function(…) {…}. Function Declaration легче «ловятся глазами».

…Но если Function Declaration нам не подходит по какой-то причине или нам нужно условное объявление (мы рассмотрели это в примере выше), то следует использовать Function Expression.

5. Что такое функции высшего порядка? Приведите примеры.

Функции высшего порядка — это функции, которые могут:

  1. Принимать функции в качестве аргументов.
  2. Возвращать функции как результат своей работы.

Вот встроенные функции высшего порядка в JavaScript:

  • map() — применяет функцию ко всем элементам массива.
  • filter() — фильтрует элементы массива на основе условия.
  • reduce() — сводит массив к одному значению, применяя функцию.

6. Что такое Promise и какие бывают состояния?

Promise — объект в JavaScript, который представляет результат асинхронной операции. Промис позволяет обрабатывать результат операции, когда он станет доступным, вместо того, чтобы блокировать выполнение кода и ожидать завершения операции.

Промис может находиться в одном из трех состояний:

  • Pending — исходное состояние промиса. Он находится в ожидании выполнения или отклонения операции.
  • Fulfilled — промис переходит в это состояние, когда операция успешно завершается.
  • Rejected — промис переходит в это состояние, когда операция завершается с ошибкой. Здесь промис возвращает причину ошибки.

Пример:

			function fetchData() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const data = 'Some data';
      // Имитация успешного выполнения операции
      resolve(data);
      // Имитация ошибки
      // reject('Error occurred');
    }, 2000);
  });
}
fetchData()
  .then(result => {
    console.log('Результат:', result);
  })
  .catch(error => {
    console.log('Ошибка:', error);
  });

		

7. Как сравнивать в JS? Как сравнить два массива?

Когда вы сравниваете примитивные типы (числа, строки, булевы значения и т.д.), используются операторы == (нестрогое сравнение) и === (строгое сравнение).

== проверяет равенство значений после приведения типов.

=== проверяет как равенство значений, так и типов (без приведения типов).

При сравнении массивов в JavaScript важно помнить, что массивы — это объекты, и сравнение происходит по ссылке. То есть два массива будут равны только в том случае, если они указывают на одну и ту же ссылку в памяти.

Можно преобразовать массивы в строки JSON и сравнить их. Это удобный способ, но он имеет несколько ограничений (например, порядок элементов в массиве имеет значение).

Для более точного сравнения массивов (в том числе с вложенными структурами) можно пройтись по каждому элементу массива с помощью цикла for или методов every/some.

8. Чем отличаются any, unknown и never?

Основные различия:

  • any: можно присваивать любые значения, без проверки типа.
  • unknown: можно присваивать любые значения, но необходимо проверять тип перед использованием.
  • never: тип для значений, которые не могут существовать, например, функции, которые не возвращают ничего.

9. Что такое CORS и как его можно решить на фронтенде?

CORS — механизм безопасности, который предотвращает доступ к данным с других источников. Чтобы решить проблемы с CORS:

  • На фронтенде можно использовать прокси-серверы для разработки.
  • В продакшн-окружении необходимо настроить CORS на сервере.
  • Если вы не контролируете сервер, то решением будет обращение к разработчикам API или использование серверного прокси для обхода ограничений.

10. Что такое Progressive Web Apps (PWA)? Зачем нужно?

Progressive Web Apps (PWA) — тип веб-приложений, который использует современные веб-технологии для предоставления пользователям опыта, схожего с нативными мобильными приложениями, но через обычный веб-браузер. PWA можно использовать в любом браузере, который поддерживает стандарты веба. Такие приложения могут работать оффлайн, присылать пуши, быстро загружаться и подстраиваться под любой браузер, который поддерживает эти фичи. Из примеров — банковские веб-приложения, которые можно установить на экран.

Вот главные особенности PWA:

  • Работа в оффлайн-режиме (Offline-first). PWA используют Service Workers, которые позволяют веб-приложению работать даже без интернета. Это значит, что приложение может кешировать данные и ресурсы, обеспечивая доступность контента в оффлайн-режиме или при плохом интернет-соединении.
  • Установка на устройство (Installable). PWA можно устанавливать на устройства как нативные приложения. При этом скачивать их из магазина пользователю не нужно.
  • Push-уведомления. PWA могут отправлять push-уведомления — так пользователь может получать информацию, даже если приложение закрыто. 
  • Обновления. Приложения могут автоматически обновляться в фоновом режиме.
  • Мгновенная загрузка. PWA загружаются быстрее, так как они используют кэширование ресурсов и данных через Service Workers — это улучшает производительность даже при медленном интернете.
  • Универсальность. PWA работают на всех платформах (мобильные устройства, десктопы, планшеты) и в любых браузерах, которые поддерживают современные веб-стандарты (например, Chrome, Firefox, Safari, Edge и т.д.).

11. Lifecycle во Vue: когда использовать хуки жизненного цикла?

В Vue 2 жизненный цикл выглядит следующим образом:

beforeCreatecreatedbeforeMountmountedbeforeUpdateupdatedbeforeDestroyDestroyed

В Vue 3 жизненный цикл был немного изменен и улучшен. Некоторые хуки переименованы:

beforeDestroybeforeUnmount

destroyedunmounted

Кроме того, в Vue 3 появилась возможность использовать Composition API, который предоставляет более гибкий способ организации жизненного цикла компонентов. Теперь можно создавать компоненты с помощью функции setup().

Когда использовать хуки жизненного цикла?

  • beforeCreate и created используются для настройки данных, свойств или методов компонента до и после его создания.
  • beforeMount и mounted полезны для операций, которые необходимо выполнить при рендере компонента (например, вызов API, настройка сторонних библиотек, или выполнение начальной логики).
  • beforeUpdate и updated можно использовать для наблюдения за изменениями данных или для манипуляций с DOM до или после его обновления.
  • beforeDestroy и destroyed (или beforeUnmount и unmounted в Vue 3) полезны для очистки ресурсов перед уничтожением компонента.

12. В каком компоненте отработает mounted вначале — дочернем или родительском?

В Vue.js хук mounted вызывается для компонента, как только его DOM вставляется в дерево DOM. Когда компонент — часть иерархии (например, родительский и дочерний компоненты), хуки жизненного цикла выполняются в определенном порядке.

Хук mounted будет вызван для дочернего компонента перед родительским.

Объяснение: родительский компонент инициирует рендеринг и монтирование дочернего компонента. Когда Vue монтирует компоненты, он сначала монтирует все дочерние компоненты, а потом родительский компонент. Это связано с тем, что родительский компонент ждет завершения монтирования дочерних компонентов, чтобы их правильно вставить в DOM.

13. Как изменить commit message

1. Изменить последний коммит

Если нужно изменить сообщение только последнего коммита, используйте команду:

git commit --amend

После этого Git откроет редактор, где вы сможете изменить сообщение коммита. Сохраните изменения и выйдите из редактора.

2. Изменить старый коммит

Если нужно изменить сообщение не последнего коммита, а более старого, можно воспользоваться rebase:

git rebase -i <commit_id>

Здесь <commit_id> — идентификатор коммита, сообщение которого вы хотите изменить. Можно использовать git log для получения ID нужного коммита. В открывшемся файле замените слово pick на reword напротив коммита, который хотите изменить, и сохраните файл. Git откроет редактор для изменения сообщения этого коммита. После редактирования сохраните и выйдите.

Завершите процесс rebase:

git rebase --continue

3. Изменить сообщение в уже отправленном коммите (Push)

Если коммит был уже отправлен в удаленный репозиторий, а вы хотите изменить сообщение коммита, то вам нужно будет форсировать пуш.

Измените коммит с помощью git commit --amend или git rebase -i.

После изменения выполните принудительный пуш:

git push --force

Пожалуйста, перед собеседованием повторите CSS. Многие разработчики усиленно готовятся к технической сессии по JS, TS, своему фреймворку, но забывают о верстке. Если вам повезет работать над новым продуктом, то верстка будет занимать больше половины времени.

14. Что такое псевдоклассы в CSS? Как обратиться к псевдоклассу в JS?

Псевдоклассы в CSS — стили, которые применяются к элементам в зависимости от их состояния или структуры в документе.

В JavaScript нельзя напрямую изменить псевдоклассы, но можно использовать селекторы CSS для выбора элементов с псевдоклассами. Это делается с помощью методов по типу querySelector и querySelectorAll.

15. Что такое dvh, lvh, em и rem в CSS? Когда и почему лучше использовать каждую из этих единиц измерения?

Эти единицы были введены в спецификации CSS, чтобы улучшить работу с адаптивным дизайном и обеспечить корректное отображение на мобильных устройствах.

  • dvh (dynamic viewport height) — единица измерения, которая учитывает динамические изменения в размерах окна, например, высота при открытии клавиатуры на мобильных устройствах. 
  • lvh (large viewport height) — единица измерения, которая использует максимальную высоту вьюпорта, независимо от динамических изменений (например, не учитывает изменения, связанные с клавиатурой на мобильных устройствах).

Когда использовать:

  • dvh полезен, когда нужно учитывать изменения размера окна при взаимодействии с мобильными устройствами (например, при открытии клавиатуры).
  • lvh полезен для работы с мобильными устройствами, когда нужно обеспечить стабильный дизайн, не зависящий от изменения высоты вьюпорта из-за динамических изменений.
  • em — это единица измерения, которая зависит от размера шрифта родительского элемента. 1em равен текущему размеру шрифта родительского элемента. Если в родительском элементе установлен шрифт размером 16px, то 1em будет равен 16px.
  • rem — это единица измерения, которая зависит от размера шрифта корневого элемента (<html>). 1rem равен размеру шрифта, установленному на корневом элементе (по умолчанию в большинстве браузеров это 16px).

Когда и зачем использовать каждую из этих единиц?

  • dvh/lvh: Используйте, если вам нужно учитывать динамическое изменение высоты вьюпорта (например, на мобильных устройствах при открытии клавиатуры) или если вам нужно работать с максимальной высотой вьюпорта, независимо от динамических изменений.
  • em: Используйте, когда размеры элементов должны зависеть от размера шрифта родительского элемента. Это полезно для создания гибких и адаптивных макетов, которые изменяются с размером шрифта.
  • rem: Используйте, когда нужно, чтобы размеры были относительно размера шрифта корневого элемента. Так проще поддерживать единообразие и легко масштабировать весь интерфейс.
Если вы знали ответы на все вопросы, поздравляем — можете гордо считать себя мидлом и смело ходить на собеседования. Если в каких-то вопросах плаваете, то подучите теорию и порешайте побольше задачек/почитайте код других разработчиков — тогда все получится.
На все ответили?
Да, я такое как орешки щелкаю!
На что-то ответил, но вопросы не из легких...
Следите за новыми постами
Следите за новыми постами по любимым темам
3К открытий20К показов