Перейти к основному содержимому

Junior JavaScript

Экспресс-шпаргалка 20/20

  1. var имеет функциональную область видимости и hoisting с undefined. let и const имеют блочную область видимости и TDZ. const нельзя переназначить.
  2. == сравнивает с приведением типов, === сравнивает без приведения. В прод-коде почти всегда используют ===.
  3. undefined обычно означает "значение не задано", null — "значение задано как пустое намеренно".
  4. JavaScript всегда копирует и передаёт значения. Для объекта таким значением является ссылка, поэтому две переменные могут указывать на один объект.
  5. Hoisting — поднятие объявлений вверх области видимости. TDZ — период до инициализации let/const, когда доступ к переменной запрещен.
  6. this определяется местом вызова функции, а не местом ее объявления.
  7. Замыкание — функция, которая сохраняет доступ к переменным внешней функции после ее завершения.
  8. Если свойства нет у объекта, движок ищет его в прототипе, затем в прототипе прототипа и так далее до null.
  9. Event Loop координирует выполнение call stack и очередей задач, чтобы однопоточный JS мог обрабатывать асинхронные операции.
  10. async/await — синтаксис поверх Promise, который делает асинхронный код более читаемым.
  11. Microtasks выполняются сразу после текущего синхронного кода и до следующей macrotask.
  12. map преобразует каждый элемент, filter отбирает элементы по условию, reduce сводит массив к одному значению.
  13. Debounce откладывает вызов до паузы в событиях, throttle ограничивает частоту вызова фиксированным интервалом.
  14. Shallow copy копирует только первый уровень, deep copy — все вложенные уровни.
  15. ... в выражении — spread (раскрывает), в параметрах/деструктуризации — rest (собирает остаток).
  16. Деструктуризация позволяет извлекать значения из объектов и массивов в переменные.
  17. Иммутабельность делает изменения состояния предсказуемыми, упрощает отладку и помогает оптимизациям рендера.
  18. Cookie отправляются на сервер с запросом, localStorage живет между сессиями, sessionStorage очищается при закрытии вкладки.
  19. CORS — механизм браузера, который регулирует междоменные запросы через специальные HTTP-заголовки.
  20. Обычный script блокирует парсинг HTML. async загружается параллельно и выполняется сразу после загрузки. defer загружается параллельно и выполняется после парсинга DOM в порядке подключения.

Практический мост: вопросы 1-5

Эти пять вопросов закрывают первый блок урока 1 JavaScript: scope, сравнения, отсутствие значения, ссылки и TDZ. Идите по порядку: прочитать урок, ответить карточку, закрепить кодом, проверить себя на edge-case.

ШагНавыкОтветитьПрактика
1Scope, reassignment, TDZq-1, q-5Песочница: "Две суммы": решите через const seen = new Map() и let i, затем объясните, почему const не замораживает Map.
2Strict equality и coercionq-2Свободная практика: напишите isFilled(value) и заранее выпишите результат для 0, '0', false, null, undefined, ''.
3Контракт null/undefinedq-3Свободная практика: нормализуйте API-пользователя, где avatarUrl может отсутствовать, быть null или быть пустой строкой.
4Ссылки и мутацияq-4Песочница: "Перенос нулей в конец": верните новый массив и докажите, что входной массив не изменился.
5Предсказание выполненияq-5Свободная практика: перед запуском предскажите вывод и ошибки для 5 коротких фрагментов с var, let, const и function declaration.

Кластер считается закрытым, когда вы можете без подсказок предсказать вывод 10 сниппетов, назвать production-баг для каждого вопроса и объяснить, какой edge-case проверяет ваш пример.

1. В чем разница между var, let и const?

Теги: javascript, variables, scope, hoisting Сложность: Junior

Короткий ответ

var имеет функциональную область видимости и hoisting с undefined. let и const имеют блочную область видимости и TDZ. const нельзя переназначить.

Что сказать на интервью (30-60 секунд)

  1. var живет в функции, а let и const живут в блоке: if, for, {}. Поэтому var может неожиданно "утечь" за пределы блока.
  2. var поднимается и до строки объявления дает undefined; let и const тоже попадают в область видимости, но до инициализации находятся в TDZ и дают ReferenceError.
  3. const запрещает переназначить переменную, но не делает объект или Map immutable. Поэтому const user = {} можно мутировать через user.name = ....
  4. Практическое правило: по умолчанию const, если нужно переназначение - let, var в новом коде почти всегда избегаем.

Мини-пример

var a = 1;
let b = 2;
const c = { value: 3 };
c.value = 4;

Углубление (2-3 минуты)

  1. Production-сценарий: обработчики в цикле с var могут получить одно и то же финальное значение индекса, потому что переменная общая для всей функции.
  2. Контрпример: const config = {}; config.theme = 'dark' работает, но config = {} падает. Это показывает разницу между binding и содержимым объекта.
  3. Практика: в задаче "Две суммы" используйте const seen = new Map() и let i. Объясните, почему seen.set(...) разрешен, а seen = new Map() нет.

Типичные ошибки

  1. Говорят, что const делает объект неизменяемым. Это неверно без Object.freeze или иммутабельного подхода.
  2. Говорят, что let и const "не hoist-ятся". Корректнее: они hoist-ятся, но недоступны в TDZ.
  3. Ожидают от var блочную область видимости внутри if или for.

Follow-up вопросы

  1. Что выведут три setTimeout внутри for с var i, и что изменится с let i?
  2. Почему const arr = [] не мешает выполнить arr.push(1)?
  3. Как бы вы защитили объект от мутаций, если это действительно требуется?

Что повторить

  1. Урок 1: Hoisting, scope, TDZ.
  2. Мини-код: цикл с var/let, доступ к переменной до объявления, мутация объекта в const.
  3. Песочница: "Две суммы" - проговорите выбор const и let в своем решении.

Связанные модули и карта

  1. Обучение: JavaScript, урок 1
  2. Обучение: Web и сеть
  3. Карта подготовки: JavaScript

2. В чем разница между == и ===?

Теги: javascript, equality, type-coercion, operators Сложность: Junior

Короткий ответ

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

Что сказать на интервью (30-60 секунд)

  1. === сравнивает без приведения типов: тип и значение должны совпасть. == сначала пытается привести типы по правилам Abstract Equality.
  2. В рабочем коде почти всегда выбираю ===, потому что 0 == false, '' == false и '0' == 0 часто маскируют ошибку данных.
  3. Осознанное исключение встречается редко: например, value == null, если специально хотим поймать и null, и undefined.
  4. Проверяю таблицей edge-cases, а не одним примером: 0, '0', false, '', null, undefined, NaN.

Мини-пример

0 == false; // true
0 === false; // false

Углубление (2-3 минуты)

  1. Production-сценарий: форма присылает userId строкой '0', а код сравнивает с числом 0. С == баг может пройти незаметно, с === станет видно, что контракт данных смешан.
  2. Для проверок наличия значения отделяйте "пусто" от "ложно": if (value) сломается на 0, а явная проверка value !== null && value !== undefined сохранит смысл.
  3. Практика: в свободной практике напишите isFilled(value) и отдельно решите, какие значения должны считаться заполненными в вашем сценарии.

Типичные ошибки

  1. Говорят "всегда только ===" и не могут объяснить редкий осознанный кейс value == null.
  2. Смешивают truthy/falsy с equality: if (value) - это не то же самое, что value === true.
  3. Не проверяют NaN: даже NaN === NaN дает false, для него нужен Number.isNaN.

Follow-up вопросы

  1. Почему 0 == false true, а 0 === false false?
  2. Чем опасна проверка if (count) для счетчика, который может быть равен 0?
  3. Когда вы бы допустили value == null, и как прокомментировали бы это в коде?

Что повторить

  1. Урок 1: Типы и сравнения без магии.
  2. Таблицу edge-cases: 0, '0', false, '', null, undefined, NaN, [].
  3. Свободная практика: реализуйте isFilled(value) и объясните каждое решение до запуска.

Связанные модули и карта

  1. Обучение: JavaScript, урок 1
  2. Обучение: Web и сеть
  3. Карта подготовки: JavaScript

3. Чем null отличается от undefined?

Теги: javascript, null, undefined, types Сложность: Junior

Короткий ответ

undefined обычно означает "значение не задано", null — "значение задано как пустое намеренно".

Что сказать на интервью (30-60 секунд)

  1. undefined чаще означает, что значение не передали, свойство отсутствует или переменную еще не присвоили.
  2. null обычно означает осознанное пустое значение: поле есть, но сейчас оно пустое.
  3. В API и UI это разные состояния. Например, avatarUrl: undefined может значить "не обновлять поле", а avatarUrl: null - "очистить аватар".
  4. Проверяю не только значение, но и наличие свойства: 'avatarUrl' in user отличается от user.avatarUrl === undefined.

Мини-пример

let a;
const obj = { v: null };
console.log(a, obj.missing, obj.v);

Углубление (2-3 минуты)

  1. Production-сценарий: PATCH формы профиля. Если поле отсутствует - оставляем старое значение; если пришел null - явно очищаем.
  2. ?? помогает отличать null/undefined от валидных falsy-значений вроде 0 и '', но контракт все равно нужно проговорить.
  3. Практика: в свободной практике напишите normalizeUser(input), который различает отсутствующий avatarUrl, avatarUrl: null и avatarUrl: ''.

Типичные ошибки

  1. Используют if (!value) и случайно смешивают null, undefined, 0, false и пустую строку.
  2. Не различают "свойства нет" и "свойство есть, но значение undefined".
  3. Меняют контракт API без миграции: клиенты начинают по-разному трактовать пустое значение.

Follow-up вопросы

  1. Чем отличаются obj.missing, obj.value = undefined и obj.value = null?
  2. Когда лучше вернуть пустой массив вместо null?
  3. Какой тест докажет, что PATCH не стирает поле случайно?

Что повторить

  1. Урок 1: Типы и сравнения без магии.
  2. ??, optional chaining, оператор in, явные проверки value === null и value === undefined.
  3. Свободная практика: нормализация API-объекта с отсутствующим, null и пустым значением.

Связанные модули и карта

  1. Обучение: JavaScript, урок 1
  2. Обучение: Web и сеть
  3. Карта подготовки: JavaScript

4. Что такое примитивные и ссылочные типы?

Теги: javascript, primitives, references, mutation Сложность: Junior

Короткий ответ

JavaScript всегда копирует и передаёт значения. У примитива копируется само значение, а у объекта - значение-ссылка, поэтому две переменные могут указывать на один объект.

Что сказать на интервью (30-60 секунд)

  1. Примитивы (string, number, boolean, null, undefined, symbol, bigint) передаются и сравниваются как значения.
  2. Для объекта передаётся значение-ссылка. Функция получает копию этой ссылки, поэтому const b = a; b.x = 2 меняет один объект, доступный и через a, и через b; переназначение b при этом не переназначит a.
  3. В UI это важно для state: скрытая мутация массива или объекта может не дать ожидаемого обновления и усложнит отладку.
  4. Проверяю примером, где функция получает объект, мутирует его и после вызова видно, изменился ли оригинал.

Мини-пример

const a = { x: 1 };
const b = a;
b.x = 2;
console.log(a.x); // 2

Углубление (2-3 минуты)

  1. Production-сценарий: компонент мутирует items.push(newItem) вместо создания нового массива. Данные изменились, но сравнение ссылок не показывает новое значение как отдельное состояние.
  2. Для shallow copy через { ...obj } или [...arr] копируется только первый уровень. Вложенные объекты остаются общими ссылками.
  3. Практика: решите "Перенос нулей в конец" так, чтобы функция вернула новый массив и не изменила входной nums.

Типичные ошибки

  1. Думают, что const защищает объект от внутренней мутации.
  2. Считают shallow copy полноценной deep copy и случайно делят вложенные объекты между состояниями.
  3. Сравнивают объекты через === и ожидают сравнение по содержимому.

Follow-up вопросы

  1. Почему { x: 1 } === { x: 1 } дает false?
  2. Что изменится после const copy = [...items], если внутри items лежат объекты?
  3. Какой тест докажет, что функция не мутирует входной массив?

Что повторить

  1. Урок 1: reference equality vs value equality.
  2. Мини-код: const b = a, shallow copy, вложенный объект, сравнение двух одинаковых литералов.
  3. Песочница: "Перенос нулей в конец" - добавьте ручную проверку, что исходный массив остался прежним.

Связанные модули и карта

  1. Обучение: JavaScript, урок 1
  2. Обучение: Web и сеть
  3. Карта подготовки: JavaScript

5. Что такое hoisting и TDZ?

Теги: javascript, hoisting, tdz, execution-context Сложность: Junior

Короткий ответ

Hoisting — поднятие объявлений вверх области видимости. TDZ — период до инициализации let/const, когда доступ к переменной запрещен.

Что сказать на интервью (30-60 секунд)

  1. Hoisting - это этап подготовки области видимости перед выполнением кода: движок заранее регистрирует объявления.
  2. var при этом инициализируется как undefined, поэтому чтение до строки объявления не падает, но дает неготовое значение.
  3. let и const тоже известны движку заранее, но находятся в TDZ до фактической строки объявления. Чтение в TDZ дает ReferenceError.
  4. Function declaration можно вызвать до объявления, а function expression в const подчиняется TDZ как обычная переменная.

Мини-пример

console.log(x); // undefined
var x = 1;
// console.log(y); // ReferenceError
let y = 2;

Углубление (2-3 минуты)

  1. Production-сценарий: код читает конфиг или handler до инициализации. С var баг может превратиться в тихий undefined, с let/const ошибка проявится сразу.
  2. TDZ полезна тем, что не дает использовать переменную до явной инициализации, особенно в длинных блоках и условных ветках.
  3. Практика: в свободной практике перед запуском подпишите рядом с каждым сниппетом: "вывод", "ReferenceError" или "работает".

Типичные ошибки

  1. Объясняют hoisting как физическое "перемещение строк кода". Надежнее говорить про создание bindings до выполнения.
  2. Смешивают var hoisting и function declaration hoisting.
  3. Забывают, что typeof value в TDZ для let/const тоже может бросить ReferenceError.

Follow-up вопросы

  1. Что произойдет при console.log(a); var a = 1 и почему?
  2. Почему console.log(b); let b = 1 падает, хотя объявление есть ниже?
  3. Чем отличается foo() до function foo() {} от bar() до const bar = () => {}?

Что повторить

  1. Урок 1: Hoisting, scope, TDZ.
  2. Мини-код: var до объявления, let до объявления, function declaration до объявления, function expression в const.
  3. Свободная практика: соберите 5 сниппетов, сначала предскажите результат письменно, потом запускайте.

Связанные модули и карта

  1. Обучение: JavaScript, урок 1
  2. Обучение: Web и сеть
  3. Карта подготовки: JavaScript

Практический мост: вопросы 6-10

Этот блок закрывает вторую часть урока 1 JavaScript и урок 2 про async/runtime. Цель - не выучить термины, а научиться предсказывать выполнение кода и объяснять реальные баги.

ШагНавыкОтветитьПрактика
1this в точке вызоваq-6Свободная практика: сравните method call, detached function, bind и arrow callback на одном объекте user.
2Closure и сохранение окруженияq-7Свободная практика: реализуйте createCounter() и once(fn), затем докажите, что состояние не глобальное.
3Prototype lookupq-8Свободная практика: создайте объект через Object.create, проверьте in, hasOwnProperty и Object.keys.
4Event Loopq-9Свободная практика: заранее выпишите порядок логов для sync, Promise.then, queueMicrotask, setTimeout.
5Promise и async/awaitq-10Свободная практика: напишите fetchWithTimeout(url, ms) с AbortController и обработкой ошибки отмены.

Кластер считается закрытым, когда вы можете до запуска предсказать вывод async-сниппета, объяснить потерю this, показать closure без глобального состояния и назвать один production-баг для каждого вопроса.

6. Как работает this в JavaScript?

Теги: javascript, this, bind, call-apply Сложность: Junior

Короткий ответ

this определяется местом вызова функции, а не местом ее объявления. У arrow function своего this нет: она берет его из внешней области.

Что сказать на интервью (30-60 секунд)

  1. В обычной функции this зависит от call-site: user.hi() и const fn = user.hi; fn() дают разный контекст.
  2. В strict mode detached function получает this === undefined; без strict mode в браузере можно случайно попасть в global object.
  3. bind, call и apply явно задают this, а arrow function не подходит для методов объекта, которым нужен динамический receiver.
  4. Production-риск: callback передали без привязки, handler потерял объект и начал читать undefined вместо нужного state.

Мини-пример

const user = { name: 'Ann', hi() { return this.name; } };
const fn = user.hi;
fn(); // undefined в strict mode
user.hi(); // "Ann"

Углубление (2-3 минуты)

  1. Production-сценарий: метод класса или объекта передали как onClick={user.hi}. При вызове handler уже не вызывается как user.hi(), поэтому контекст потерян.
  2. Контрпример: user.hi.bind(user) фиксирует контекст, но arrow method внутри объекта не получает this от объекта.
  3. Практика: в свободной практике сделайте один объект user и четыре вызова: method call, detached call, bind, arrow callback. Перед запуском подпишите ожидаемый результат.

Типичные ошибки

  1. Говорят, что this определяется местом объявления функции.
  2. Используют arrow function как метод объекта и ждут, что this станет самим объектом.
  3. Объясняют bind/call/apply как одно и то же, не называя разницу в моменте вызова и формате аргументов.

Follow-up вопросы

  1. Что изменится между button.addEventListener('click', user.hi) и button.addEventListener('click', user.hi.bind(user))?
  2. Почему const fn = user.hi; fn() теряет this?
  3. Когда arrow callback помогает сохранить внешний this, а когда ломает метод объекта?

Что повторить

  1. Урок 1: this и prototype chain.
  2. Мини-код: method call, detached function, bind, call, apply, arrow callback.
  3. Свободная практика: объясните каждый вывод до запуска и затем сравните с фактическим результатом.

Связанные модули и карта

  1. Обучение: JavaScript, урок 1
  2. Обучение: Web и сеть
  3. Карта подготовки: JavaScript

7. Что такое замыкание?

Теги: javascript, closures, lexical-scope, memory Сложность: Junior

Короткий ответ

Замыкание - это функция, которая сохраняет доступ к переменным внешней функции после завершения этой внешней функции.

Что сказать на интервью (30-60 секунд)

  1. Closure возникает, когда внутренняя функция использует переменные из lexical scope внешней функции.
  2. Переменная хранится не как копия значения на момент создания, а как доступ к окружению, поэтому состояние может меняться между вызовами.
  3. Типичные применения: счетчики, memoization, debounce/throttle, приватное состояние без глобальных переменных.
  4. Production-риск: случайно удержали большую структуру в closure и получили утечку памяти или stale state.

Мини-пример

function counter() {
let n = 0;
return () => ++n;
}

const next = counter();
next(); // 1
next(); // 2

Углубление (2-3 минуты)

  1. Production-сценарий: debounce хранит timerId в closure, чтобы следующий вызов мог отменить предыдущий таймер.
  2. Контрпример: если положить n в global scope, несколько счетчиков начнут делить одно состояние; с closure у каждого counter свое окружение.
  3. Практика: в свободной практике реализуйте createCounter() и once(fn). Проверьте два отдельных counter и повторный вызов once.

Типичные ошибки

  1. Говорят, что closure - это просто "функция внутри функции", хотя важен доступ к внешним переменным.
  2. Не различают lexical scope и this: closure сохраняет переменные, но не "чинит" потерянный this.
  3. Забывают про memory risk: closure может удерживать объект дольше, чем нужно.

Follow-up вопросы

  1. Почему два вызова counter() создают независимое состояние?
  2. Как closure помогает реализовать debounce?
  3. Какой пример покажет, что closure может хранить stale value?

Что повторить

  1. Урок 1: Closure.
  2. Мини-код: createCounter, once(fn), debounce с timerId в closure.
  3. Свободная практика: докажите, что состояние не лежит в global scope.

Связанные модули и карта

  1. Обучение: JavaScript, урок 1
  2. Обучение: Web и сеть
  3. Карта подготовки: JavaScript

8. Что такое prototype chain?

Теги: javascript, prototypes, inheritance, objects Сложность: Junior

Короткий ответ

Prototype chain - это цепочка поиска свойства: сначала own property объекта, затем его prototype, затем prototype прототипа и так до null.

Что сказать на интервью (30-60 секунд)

  1. Если свойства нет на самом объекте, JS ищет его выше по [[Prototype]].
  2. Prototype chain влияет на чтение свойства, но не означает, что свойство скопировано в объект.
  3. in проверяет own и inherited свойства, а Object.hasOwn или hasOwnProperty проверяют только own property.
  4. Production-риск: код принимает inherited property за реальные данные объекта и ошибается при сериализации, валидации или переборе ключей.

Мини-пример

const animal = { sound: '...' };
const dog = Object.create(animal);
dog.name = 'Rex';

console.log(dog.sound); // ...
console.log(Object.keys(dog)); // ['name']

Углубление (2-3 минуты)

  1. Production-сценарий: валидация payload проверяет 'role' in user, но role пришел из prototype, а не из данных. Надежнее проверять own property.
  2. Контрпример: dog.sound читается, но Object.keys(dog) не показывает sound, потому что это inherited property.
  3. Практика: в свободной практике создайте dog через Object.create(animal) и сравните in, Object.hasOwn(dog, 'sound'), Object.keys(dog).

Типичные ошибки

  1. Думают, что prototype chain копирует свойства в объект.
  2. Путают prototype chain с class inheritance и не могут объяснить lookup на простом объекте.
  3. Используют for...in без проверки own property и случайно обрабатывают inherited keys.

Follow-up вопросы

  1. Почему dog.sound есть, а Object.keys(dog) не показывает sound?
  2. Чем отличается 'sound' in dog от Object.hasOwn(dog, 'sound')?
  3. Что может пойти не так при переборе объекта через for...in?

Что повторить

  1. Урок 1: Prototype chain.
  2. Мини-код: Object.create, in, Object.hasOwn, Object.keys, for...in.
  3. Свободная практика: объясните, где свойство лежит физически и как это проверить.

Связанные модули и карта

  1. Обучение: JavaScript, урок 1
  2. Обучение: Web и сеть
  3. Карта подготовки: JavaScript

9. Что такое Event Loop?

Теги: javascript, event-loop, async, tasks Сложность: Junior

Короткий ответ

Event Loop управляет порядком выполнения: сначала синхронный call stack, затем microtasks, затем следующая macrotask.

Что сказать на интервью (30-60 секунд)

  1. JS выполняет один синхронный стек за раз; async API только планируют продолжение работы.
  2. После завершения текущего sync-кода движок очищает microtask queue (Promise.then, queueMicrotask), и только потом берет macrotask (setTimeout, события, I/O).
  3. Это объясняет порядок логов и помогает разбирать гонки, зависшие UI-обновления и неожиданный порядок state updates.
  4. Production-риск: код ожидает, что setTimeout(..., 0) выполнится "сразу", но перед ним могут пройти все microtasks.

Мини-пример

console.log(1);
setTimeout(() => console.log(2), 0);
Promise.resolve().then(() => console.log(3));
console.log(4); // 1, 4, 3, 2

Углубление (2-3 минуты)

  1. Production-сценарий: после клика вы ставите setTimeout для UI-эффекта, но Promise chain обновляет state раньше, поэтому визуальный порядок отличается от ожидаемого.
  2. Контрпример: если добавить несколько Promise.then, они выполнятся до первого setTimeout, даже когда timeout равен 0.
  3. Практика: в свободной практике соберите сниппет с sync log, queueMicrotask, Promise.then и setTimeout. Сначала выпишите порядок, потом запускайте.

Типичные ошибки

  1. Думают, что setTimeout(fn, 0) выполняется до Promise.then.
  2. Называют все async-операции "очередью", не различая microtask и macrotask.
  3. Не учитывают, что длинный sync-код блокирует обработку любых очередей.

Follow-up вопросы

  1. Почему Promise.then выполнится раньше setTimeout(..., 0)?
  2. Что будет, если синхронный цикл выполняется 2 секунды?
  3. Как Event Loop помогает объяснить race condition в UI?

Что повторить

  1. Урок 2: Event Loop.
  2. Мини-код: sync logs, queueMicrotask, Promise.then, setTimeout.
  3. Свободная практика: предскажите порядок 5 сниппетов и отметьте, где microtask queue очищается полностью.

Связанные модули и карта

  1. Обучение: JavaScript, урок 2
  2. Обучение: Web и сеть
  3. Карта подготовки: JavaScript

10. Разница между Promise и async/await?

Теги: javascript, promises, async-await, error-handling Сложность: Junior

Короткий ответ

async/await - это синтаксис поверх Promise: он делает async-код похожим на последовательный, но результат async функции все равно Promise.

Что сказать на интервью (30-60 секунд)

  1. Promise описывает будущий результат: pending, затем fulfilled или rejected.
  2. async функция всегда возвращает Promise, даже если внутри возвращается обычное значение.
  3. await приостанавливает выполнение текущей async-функции до settle Promise, но не блокирует весь JS-поток.
  4. Ошибки с await обычно ловят через try/catch; в .then() цепочке нужен .catch() или второй callback.
  5. Production-риск: забыли await или обработку rejection, UI показывает успех до завершения запроса или теряет ошибку.

Мини-пример

async function load() {
try {
const res = await fetch('/api');
return await res.json();
} catch (error) {
return { error: error.message };
}
}

Углубление (2-3 минуты)

  1. Production-сценарий: форма вызывает saveUser() без await, показывает "сохранено", а запрос падает позже. Пользователь видит ложный успех.
  2. Контрпример: return fetch(url) и return await fetch(url) похожи, но try/catch внутри async-функции поймает ошибку только если Promise awaited.
  3. Практика: в свободной практике реализуйте fetchWithTimeout(url, ms) через AbortController; отдельно обработайте AbortError, чтобы отмена не выглядела как обычный сбой.

Типичные ошибки

  1. Думают, что await блокирует весь Event Loop.
  2. Забывают return в .then() цепочке и получают Promise с undefined.
  3. Смешивают реальные ошибки запроса, отмену и timeout в один generic catch без понятного поведения для UI.

Follow-up вопросы

  1. Что вернет async function f() { return 1; }?
  2. Почему забытый await saveUser() может сломать UX формы?
  3. Как отличить отмененный запрос от настоящей ошибки сервера?

Что повторить

  1. Урок 2: Promise combinators и cancellation.
  2. Мини-код: async return value, try/catch, .then().catch(), забытый await.
  3. Свободная практика: реализуйте fetchWithTimeout и опишите поведение для success, timeout и network error.

Связанные модули и карта

  1. Обучение: JavaScript, урок 2
  2. Обучение: Web и сеть
  3. Карта подготовки: JavaScript

Практический мост: вопросы 11-15

Цель блока - научиться предсказывать порядок выполнения, выбирать подходящий метод коллекций и объяснять, где "удобный синтаксис" ломает состояние.

ШагНавыкОтветитьПрактика
1Очереди microtask/macrotaskq-11Свободная практика: предскажите порядок логов для console.log, Promise.then, queueMicrotask и setTimeout(0), затем проверьте запуском.
2Трансформации коллекцийq-12Песочница: "Слияние двух отсортированных массивов": решите без мутации входных массивов и объясните, почему здесь не нужен reduce.
3Контроль частоты событийq-13Свободная практика: реализуйте debounce(fn, 300) и throttle(fn, 300), затем сравните логи при пяти быстрых вызовах.
4Глубина копированияq-14Песочница: "Перенос нулей в конец": верните новый массив и отдельно покажите пример, где { ...state } сохраняет ссылку на вложенный объект.
5Spread/rest в данных и функцияхq-15Свободная практика: соберите sum(...nums), mergeConfig(defaults, override) и пример, где порядок spread меняет итоговое значение.

Готовность: вы можете до запуска предсказать вывод async-примера, выбрать map/filter/reduce по форме результата, назвать баг от shallow copy и объяснить разницу между spread и rest на двух коротких фрагментах.

11. Разница microtask и macrotask?

Теги: javascript, microtask, macrotask, event-loop Сложность: Junior

Короткий ответ

Microtask выполняется после текущего синхронного кода и до следующей macrotask; macrotask - это следующий крупный шаг очереди событий, например callback setTimeout.

Что сказать на интервью (30-60 секунд)

  1. Сначала выполняется весь sync-код текущего стека.
  2. Затем runtime дренирует очередь microtasks: Promise.then, queueMicrotask, продолжение await.
  3. Только после этого берется следующая macrotask: setTimeout, пользовательское событие, часть I/O.
  4. Production-риск: код в Promise.then может изменить state раньше, чем callback таймера, поэтому неверное ожидание порядка дает мигающий UI или stale-state.
  5. Проверка: выписываю ожидаемый порядок логов, запускаю мини-фрагмент и отдельно проверяю случай с несколькими microtasks подряд.

Мини-пример

console.log('sync-1');
setTimeout(() => console.log('macro'), 0);
queueMicrotask(() => console.log('micro-1'));
Promise.resolve().then(() => console.log('micro-2'));
console.log('sync-2');
// sync-1, sync-2, micro-1, micro-2, macro

Углубление (2-3 минуты)

  1. Production-сценарий: после клика вы ставите optimistic state в Promise.then, а fallback через setTimeout; microtask сработает раньше таймера даже при 0ms.
  2. Контрпример: длинная цепочка microtasks может задержать переход к следующей macrotask и ухудшить отзывчивость.
  3. Практика: в Песочнице напишите 3 фрагмента с await, Promise.then, queueMicrotask и setTimeout(0), перед запуском запишите ожидаемый порядок.

Типичные ошибки

  1. Считают, что setTimeout(..., 0) выполнится сразу после текущей строки.
  2. Не учитывают, что все накопленные microtasks выполняются до следующей macrotask.
  3. Объясняют порядок словом "асинхронно", но не называют конкретную очередь.

Follow-up вопросы

  1. Что выведет пример, если внутри Promise.then добавить еще один Promise.then?
  2. Почему бесконечная цепочка microtasks может заморозить интерфейс?
  3. Чем await Promise.resolve() похож на Promise.then с точки зрения очереди?

Что повторить

  1. Урок 2: Event Loop.
  2. Таблицу: sync stack -> microtasks -> next macrotask.
  3. Free-practice prompt: "predict logs" для console.log, queueMicrotask, Promise.then, await, setTimeout(0).

Связанные модули и карта

  1. Обучение: JavaScript, урок 2
  2. Обучение: Web и сеть
  3. Карта подготовки: JavaScript

12. Для чего map, filter, reduce?

Теги: javascript, arrays, map-filter-reduce, functional-style Сложность: Junior

Короткий ответ

map возвращает новый массив той же длины, filter возвращает подмножество элементов, reduce сводит массив к аккумулятору любого типа.

Что сказать на интервью (30-60 секунд)

  1. Выбираю метод по форме результата: столько же элементов - map, меньше или равно - filter, одно значение/объект/словарь - reduce.
  2. Эти методы не обязаны мутировать исходный массив, поэтому хорошо подходят для подготовки данных к рендеру.
  3. Production-риск: map ради side effect или сложный reduce ухудшает читаемость и прячет баги в трансформации.
  4. Проверка: прогоняю пустой массив, один элемент и несколько элементов с ожидаемым результатом.

Мини-пример

const users = [
{ name: 'Ann', active: true, score: 10 },
{ name: 'Bob', active: false, score: 7 },
];

const names = users.map(user => user.name); // ['Ann', 'Bob']
const active = users.filter(user => user.active); // [{ name: 'Ann', ... }]
const totalScore = users.reduce((sum, user) => sum + user.score, 0); // 17

Углубление (2-3 минуты)

  1. Production-сценарий: API вернул список заказов, а UI должен показать только оплаченные заказы, их названия и общую сумму.
  2. Контрпример: если reduce превращается в 20 строк условий, лучше разбить трансформацию на именованные шаги или обычный цикл.
  3. Практика: в Песочнице: "Слияние двух отсортированных массивов" сохраните входные массивы неизменными и объясните, почему выбран цикл, а не map.

Типичные ошибки

  1. Используют map без возвращаемого значения и получают массив undefined.
  2. Мутируют объекты внутри map, ожидая "чистую" трансформацию.
  3. Выбирают reduce ради краткости, хотя результат проще читается через filter + map.

Follow-up вопросы

  1. Какой метод выберете, если нужно получить объект { id: user }, и почему?
  2. Что вернет map, если callback ничего не возвращает?
  3. Какие тесты нужны для пустого массива и массива из одного элемента?

Что повторить

  1. Урок 3: структуры данных и функциональные подходы.
  2. Разницу "форма входа" и "форма результата": same-length, subset, accumulator.
  3. Free-practice prompt: преобразуйте список заказов в { count, revenue, paidIds } без мутации входа.

Связанные модули и карта

  1. Обучение: JavaScript, урок 3
  2. Обучение: Web и сеть
  3. Карта подготовки: JavaScript

13. Что такое debounce и throttle?

Теги: javascript, debounce, throttle, performance Сложность: Junior

Короткий ответ

Debounce вызывает функцию после паузы в событиях, throttle разрешает вызывать функцию не чаще заданного интервала.

Что сказать на интервью (30-60 секунд)

  1. Debounce подходит для поиска по вводу: ждем, пока пользователь перестал печатать.
  2. Throttle подходит для scroll/resize/drag: обрабатываем поток событий с ограниченной частотой.
  3. Production-риск: без контроля частоты UI шлет лишние запросы, перегружает main thread и получает stale responses.
  4. Проверка: имитирую серию быстрых вызовов и смотрю timestamps, сколько раз реально вызвался handler.

Мини-пример

const debouncedSearch = debounce(searchUsers, 300);
const throttledScroll = throttle(saveScrollPosition, 100);

input.addEventListener('input', debouncedSearch);
window.addEventListener('scroll', throttledScroll);

Углубление (2-3 минуты)

  1. Production-сценарий: autocomplete без debounce отправляет запрос на каждый символ, а старый медленный ответ может перетереть свежий результат.
  2. Контрпример: для drag-preview debounce даст ощущение задержки, поэтому чаще нужен throttle или requestAnimationFrame.
  3. Практика: в Песочнице реализуйте debounce и throttle, вызовите каждую функцию 5 раз подряд и объясните разницу по логам времени.

Типичные ошибки

  1. Выбирают debounce для непрерывного drag/scroll, где пользователю нужна регулярная обратная связь.
  2. Не очищают таймер debounce и получают вызов после размонтирования компонента.
  3. Не учитывают leading/trailing-вызов и получают пропущенное первое или последнее действие.

Follow-up вопросы

  1. Что изменится, если debounce должен вызываться на leading edge?
  2. Как предотвратить stale response в поиске, кроме debounce?
  3. Почему throttle не гарантирует, что обработчик будет вызван с последним значением?

Что повторить

  1. Урок 2: async runtime и stale-response.
  2. Разницу debounce: "после паузы" и throttle: "не чаще интервала".
  3. Free-practice prompt: реализуйте оба helper-а и напишите 3 проверки для bursts, single call и delayed call.

Связанные модули и карта

  1. Обучение: JavaScript, урок 2
  2. Обучение: Web и сеть
  3. Карта подготовки: JavaScript

14. Разница shallow copy и deep copy?

Теги: javascript, shallow-copy, deep-copy, structuredclone Сложность: Junior

Короткий ответ

Shallow copy копирует только верхний уровень структуры, а вложенные объекты остаются теми же ссылками; deep copy копирует вложенность тоже.

Что сказать на интервью (30-60 секунд)

  1. { ...obj }, [...arr], Object.assign дают shallow copy.
  2. Если внутри есть объект или массив, новая внешняя обертка все еще указывает на старую вложенность.
  3. Production-риск: в React/Redux-like state можно "обновить" внешний объект, но случайно мутировать вложенный state и сломать мемоизацию.
  4. Проверка: меняю вложенное поле в копии и смотрю, изменился ли оригинал.

Мини-пример

const state = { user: { name: 'Ann' }, theme: 'dark' };
const nextState = { ...state };

nextState.user.name = 'Bob';
console.log(state.user.name); // 'Bob'

Углубление (2-3 минуты)

  1. Production-сценарий: компонент получает settings.notifications.email, shallow copy меняет settings, но вложенный объект остается общим.
  2. Контрпример: для плоского объекта { page: 1, sort: 'name' } shallow copy достаточно и проще deep clone.
  3. Практика: решите Песочницу: "Перенос нулей в конец" так, чтобы не мутировать входной массив, затем отдельно покажите shallow-copy баг на вложенном объекте.

Типичные ошибки

  1. Думают, что spread делает объект полностью независимым.
  2. Используют JSON.parse(JSON.stringify(obj)) без учета Date, undefined, функций и циклических ссылок.
  3. Делают deep copy всего state без причины и получают лишнюю стоимость вместо точечного immutable update.

Follow-up вопросы

  1. Что изменится, если вложенное поле будет массивом объектов?
  2. Когда structuredClone подойдет, а когда нет?
  3. Как написать проверку, которая докажет, что оригинал не мутируется?

Что повторить

  1. Урок 3: иммутабельность shallow vs deep.
  2. Разницу между новой внешней ссылкой и новыми вложенными ссылками.
  3. Free-practice prompt: обновите state.user.profile.city без мутации исходного state.

Связанные модули и карта

  1. Обучение: JavaScript, урок 3
  2. Обучение: Web и сеть
  3. Карта подготовки: JavaScript

15. Что делают spread и rest операторы?

Теги: javascript, spread-rest, destructuring, functions Сложность: Junior

Короткий ответ

Один синтаксис ... имеет две роли: spread раскрывает значения в месте использования, rest собирает оставшиеся значения в массив или объект.

Что сказать на интервью (30-60 секунд)

  1. Spread используется в вызовах, массивах и объектах: fn(...args), [...items], { ...defaults }.
  2. Rest используется в параметрах и деструктуризации: function sum(...nums), const { id, ...rest } = user.
  3. Production-риск: порядок spread в объекте решает, какие поля будут перезаписаны, а копирование остается shallow.
  4. Проверка: показываю пример, где { ...defaults, ...override } и { ...override, ...defaults } дают разные значения.

Мини-пример

const defaults = { pageSize: 20, theme: 'light' };
const override = { theme: 'dark' };

const config = { ...defaults, ...override }; // theme: 'dark'

function sum(...nums) {
return nums.reduce((total, n) => total + n, 0);
}

Углубление (2-3 минуты)

  1. Production-сценарий: конфиг виджета собирается из defaults, env и user options; неверный порядок spread перетирает пользовательскую настройку.
  2. Контрпример: const copy = [...items] копирует массив, но не копирует глубоко объекты внутри него.
  3. Практика: в Песочнице реализуйте mergeConfig(defaults, env, user) и pickPublicUser({ password, ...publicUser }), затем объясните порядок перезаписи.

Типичные ошибки

  1. Называют любой ... spread, хотя в параметрах функции это rest.
  2. Забывают, что более поздний spread в объекте перезаписывает более ранние поля.
  3. Ожидают deep copy от [...items] или { ...object }.

Follow-up вопросы

  1. Что вернет { a: 1, ...{ a: 2 }, a: 3 }?
  2. Чем function fn(...args) отличается от fn(...args)?
  3. Как spread работает с массивом объектов и почему это связано с shallow copy?

Что повторить

  1. Урок 3: структуры данных и иммутабельность.
  2. Две роли ...: раскрыть значения и собрать остаток.
  3. Free-practice prompt: напишите sum, mergeConfig и destructuring-rest для удаления приватного поля.

Связанные модули и карта

  1. Обучение: JavaScript, урок 3
  2. Обучение: Web и сеть
  3. Карта подготовки: JavaScript

Практический мост: вопросы 16-20

Цель блока - закрыть финальные browser/runtime темы: безопасное чтение данных, обновление state, хранение в браузере, CORS и загрузка скриптов.

ШагНавыкОтветитьПрактика
1Деструктуризация и safe accessq-16Песочница: "Получение значения по пути": верните undefined, если путь упирается в null/undefined, и объясните связь с optional chaining.
2Иммутабельное обновление stateq-17Свободная практика: обновите state.user.profile.city без мутации исходного state и проверьте ссылки prev === next.
3Browser storage и риск токеновq-18Свободная практика: составьте таблицу data -> storage -> срок жизни -> XSS/CSRF risk -> mitigation.
4CORS и preflightq-19Свободная практика: напишите CORS-политику для dev/stage/prod и пример preflight-ответа с Vary: Origin.
5Загрузка скриптовq-20Свободная практика: нарисуйте timeline для script, async, defer и объясните, где ломается порядок зависимых скриптов.

Готовность: вы можете безопасно читать вложенные данные, обновлять nested state без мутации, выбрать storage под риск, объяснить CORS как серверную политику и предсказать порядок выполнения скриптов.

16. Что такое деструктуризация?

Теги: javascript, destructuring, defaults, optional-chaining Сложность: Junior

Короткий ответ

Деструктуризация извлекает значения из массивов и объектов в переменные; для вложенных данных нужно учитывать undefined, defaults и optional chaining.

Что сказать на интервью (30-60 секунд)

  1. Для объекта важны имена свойств, для массива - позиции.
  2. Default value срабатывает только при undefined, но не при null.
  3. Вложенная деструктуризация бросит ошибку, если промежуточный объект отсутствует.
  4. Production-риск: компонент упадет на Cannot read properties of undefined, если API вернул неполный профиль.
  5. Проверка: прогоняю полный объект, отсутствующее поле, null и пустой массив.

Мини-пример

const user = { name: 'Sam', meta: undefined };

const { name, role = 'guest' } = user;
const city = user.meta?.city ?? 'unknown';

Углубление (2-3 минуты)

  1. Production-сценарий: API иногда не присылает profile.address, а UI должен показать fallback вместо падения страницы.
  2. Контрпример: const { meta: { city } } = user упадет, если meta равен undefined.
  3. Практика: решите Песочницу: "Получение значения по пути" и объясните, почему проверка value == null иногда удобна для остановки на null и undefined.

Типичные ошибки

  1. Думают, что default value сработает для null.
  2. Деструктурируют глубокий объект без проверки промежуточных уровней.
  3. Путают alias const { id: userId } = user с созданием поля id.

Follow-up вопросы

  1. Что вернет const { x = 1 } = { x: null }?
  2. Как безопасно достать items[0].title, если items может отсутствовать?
  3. Чем const { id: userId } = user отличается от const { id } = user?

Что повторить

  1. Урок 3: структуры данных и функциональные подходы.
  2. Связку destructuring defaults, optional chaining и nullish coalescing.
  3. Free-practice prompt: распакуйте nested API response без падения на отсутствующих полях.

Связанные модули и карта

  1. Обучение: JavaScript, урок 3
  2. Карта подготовки: JavaScript
  3. Песочница

17. Зачем нужна иммутабельность в фронтенде?

Теги: javascript, immutability, state, frontend Сложность: Junior

Короткий ответ

Иммутабельность помогает обновлять state через новые ссылки, чтобы UI и мемоизация могли надежно понять, что данные изменились.

Что сказать на интервью (30-60 секунд)

  1. Во фронтенде часто сравнивают ссылки: если ссылка не изменилась, обновление могут не заметить.
  2. Иммутабельное обновление не меняет старый объект, а создает новый объект для измененной ветки.
  3. Это упрощает undo, debugging, memo/cache и повторяемые тесты.
  4. Production-риск: мутировали nested state, компонент не перерендерился или memoized selector вернул старый результат.
  5. Проверка: сравниваю ссылки старого и нового state и убеждаюсь, что исходный объект не изменился.

Мини-пример

const nextState = {
...state,
user: {
...state.user,
profile: { ...state.user.profile, city: 'Kazan' },
},
};

Углубление (2-3 минуты)

  1. Production-сценарий: пользователь меняет город в профиле, но UI с memo не обновляется, потому что вложенный объект мутировали на месте.
  2. Контрпример: для локальной временной переменной внутри функции мутация массива может быть нормальной, если она не выходит наружу как shared state.
  3. Практика: в Песочнице обновите state.user.profile.city, затем проверьте state !== nextState, state.user !== nextState.user и отсутствие мутации старого state.

Типичные ошибки

  1. Делают shallow copy верхнего уровня, но мутируют вложенный объект.
  2. Считают, что иммутабельность означает "никогда не использовать push", хотя вопрос в ownership и видимости мутации.
  3. Создают deep clone всего state без необходимости и получают лишнюю стоимость.

Follow-up вопросы

  1. Какие ссылки должны измениться при обновлении state.user.profile.city?
  2. Когда локальная мутация безопасна?
  3. Какой тест докажет, что старый state остался неизменным?

Что повторить

  1. Урок 3: иммутабельность shallow vs deep.
  2. Разницу между mutation, immutable update и ownership данных.
  3. Free-practice prompt: обновите элемент массива объектов по id без мутации входного массива.

Связанные модули и карта

  1. Обучение: JavaScript, урок 3
  2. Карта подготовки: JavaScript
  3. Песочница

18. Разница между cookie, localStorage и sessionStorage?

Теги: web, cookies, storage, security Сложность: Junior

Короткий ответ

Cookie привязаны к HTTP-запросам и атрибутам безопасности, localStorage хранится между сессиями и доступен JS, sessionStorage доступен JS и живет в пределах вкладки.

Что сказать на интервью (30-60 секунд)

  1. Cookie автоматически отправляются на подходящий домен/path; HttpOnly cookie нельзя прочитать из JavaScript.
  2. localStorage переживает закрытие вкладки и браузера, но любой XSS может прочитать его содержимое.
  3. sessionStorage очищается при закрытии вкладки, но XSS-риск остается.
  4. Production-риск: refresh token в localStorage превращает XSS в кражу долгоживущей сессии.
  5. Проверка решения: называю срок жизни данных, кто может читать, отправляется ли на сервер автоматически и какой основной риск.

Мини-пример

localStorage.setItem('theme', 'dark');
sessionStorage.setItem('checkoutStep', 'payment');
document.cookie = 'lang=ru; Path=/; SameSite=Lax';

Углубление (2-3 минуты)

  1. Production-сценарий: тему интерфейса можно хранить в localStorage, но refresh token лучше не класть в JS-доступное хранилище.
  2. Контрпример: cookie без HttpOnly тоже доступна JS, поэтому слово "cookie" само по себе не означает безопасность.
  3. Практика: в Песочнице составьте таблицу для theme, draft form, access token, refresh token: storage, срок жизни, XSS/CSRF-риск, mitigation.

Типичные ошибки

  1. Говорят "cookie безопаснее" без упоминания HttpOnly, Secure, SameSite.
  2. Хранят секреты в localStorage, потому что "так проще восстановить сессию".
  3. Путают XSS-риск чтения JS-хранилища и CSRF-риск автоматической отправки cookie.

Follow-up вопросы

  1. Почему refresh token в localStorage опасен при XSS?
  2. Что меняет SameSite=Lax или SameSite=Strict?
  3. Где лучше хранить UI theme и почему это не то же самое, что хранение токена?

Что повторить

  1. Безопасность и хранение: storage options и риски.
  2. Карта подготовки: безопасность и хранение.
  3. Free-practice prompt: опишите storage-решение для login flow и отметьте, какие данные нельзя логировать.

Связанные модули и карта

  1. Обучение: безопасность и хранение
  2. Карта подготовки: безопасность и хранение
  3. Карта подготовки: JavaScript

19. Что такое CORS?

Теги: web, cors, http, browser-security Сложность: Junior

Короткий ответ

CORS - это браузерный механизм, который разрешает или запрещает JavaScript читать cross-origin ответ на основе HTTP-заголовков сервера.

Что сказать на интервью (30-60 секунд)

  1. CORS не "отправляет запросы", а ограничивает чтение ответа браузерным кодом.
  2. Политика задается сервером через Access-Control-Allow-*, frontend не может надежно "починить CORS" настройкой fetch.
  3. Для нестандартных методов/headers браузер сначала делает preflight OPTIONS.
  4. Production-риск: Access-Control-Allow-Origin: * в prod с чувствительными API маскирует плохую модель доступа и ломает credentials-сценарии.
  5. Проверка: смотрю Network preflight, origin, allowed methods/headers, credentials и Vary: Origin.

Мини-пример

HTTP/1.1 204 No Content
Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET,POST,OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Vary: Origin

Углубление (2-3 минуты)

  1. Production-сценарий: dev работает на localhost, stage на отдельном домене, prod на app.example.com; для каждого окружения нужен свой whitelist origin.
  2. Контрпример: CORS не заменяет AuthZ; если endpoint отдает чужие данные после валидного запроса, это серверная ошибка доступа.
  3. Практика: в Песочнице напишите CORS-политику для dev/stage/prod и пример preflight для запроса с Authorization.

Типичные ошибки

  1. Добавляют mode: 'no-cors' и думают, что получили нормальный JSON-ответ.
  2. Ставят Access-Control-Allow-Origin: * вместе с credentials-сценарием.
  3. Путают CORS, CSRF, authentication и authorization.

Follow-up вопросы

  1. Почему CORS настраивается на сервере?
  2. Когда браузер делает preflight OPTIONS?
  3. Зачем нужен Vary: Origin при динамическом allowlist?

Что повторить

  1. Web и сеть: CORS и API-контракты.
  2. Безопасность и хранение: CSRF, CORS и защита API.
  3. Free-practice prompt: разберите CORS-ошибку в DevTools по Request Headers, Response Headers и preflight.

Связанные модули и карта

  1. Обучение: Web и сеть, урок 3
  2. Карта подготовки: Web и сеть
  3. Карта подготовки: безопасность и хранение

20. В чем разница между script, async и defer?

Теги: web, script-loading, async-defer, performance Сложность: Junior

Короткий ответ

Обычный script блокирует HTML parsing, async загружается параллельно и выполняется сразу после загрузки без гарантии порядка, defer загружается параллельно и выполняется после парсинга DOM в порядке подключения.

Что сказать на интервью (30-60 секунд)

  1. Без атрибутов браузер останавливает парсинг HTML, загружает и выполняет script, затем продолжает парсинг.
  2. async хорош для независимых скриптов: analytics, виджеты, которые не зависят от порядка.
  3. defer хорош для основного app bundle: DOM уже распарсен, порядок нескольких deferred scripts сохраняется.
  4. Production-риск: зависимый script с async может выполниться раньше библиотеки и упасть в медленной сети.
  5. Проверка: рисую timeline parse -> download -> execute, тестирую с network throttling и смотрю порядок логов.

Мини-пример

<script src="/vendor.js" defer></script>
<script src="/app.js" defer></script>
<script src="/analytics.js" async></script>

Углубление (2-3 минуты)

  1. Production-сценарий: vendor.js и app.js зависят от порядка, поэтому их безопаснее грузить с defer, а независимую аналитику - с async.
  2. Контрпример: если inline script сразу читает DOM-элемент ниже по HTML, обычный blocking script может выполниться до появления элемента.
  3. Практика: в Песочнице запишите expected timeline для трех scripts: blocking, async, defer, затем объясните, почему async не подходит для зависимой пары.

Типичные ошибки

  1. Считают, что async всегда быстрее и безопаснее.
  2. Забывают, что несколько async scripts не сохраняют порядок выполнения.
  3. Не проверяют slow network, где порядок загрузки отличается от локального быстрого сценария.

Follow-up вопросы

  1. Что выбрать для основного bundle, который зависит от DOM?
  2. Почему analytics обычно можно грузить через async?
  3. Что сломается, если app.js с async выполнится раньше vendor.js?

Что повторить

  1. Карта подготовки: JavaScript, Browser runtime.
  2. Модель parse HTML -> download script -> execute script -> DOMContentLoaded.
  3. Free-practice prompt: нарисуйте timeline для script, async, defer при быстрой и медленной сети.

Связанные модули и карта

  1. Карта подготовки: JavaScript
  2. Обучение: Web и сеть
  3. Карта подготовки: Web и сеть

Куда дальше

  1. Вернитесь в модуль: JavaScript.
  2. Сверьтесь с картой темы: JavaScript.
  3. Закройте смежный модуль: Безопасность и хранение.
  4. Продолжайте по маршруту: Junior трек.
  5. Закрепите один ответ на практике в Песочнице.