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

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. Закрепите один ответ на практике в Песочнице.