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

JavaScript

Для чего модуль

Собрать не «набор фактов про язык», а рабочую инженерную модель JavaScript: как код реально выполняется, где чаще всего рождаются баги, и как это объяснять на собеседовании и в команде.

Результат после прохождения

  1. Вы объясняете поведение JS-кода через execution model, а не через заученные исключения.
  2. Вы уверенно разбираете async-сценарии: порядок выполнения, отмену, гонки, деградацию.
  3. Вы выбираете структуру данных и стиль преобразований с учетом читаемости и стоимости операций.
  4. Вы умеете довести «плавающий баг» до воспроизводимости и root cause.

Термины и аббревиатуры

ТерминКоротко
TDZВременная мертвая зона
ClosureФункция + внешнее окружение
MicrotaskПриоритетная очередь задач
Event LoopМодель выполнения задач
PrototypeМеханизм наследования

Фокус по грейдам

  1. Junior: понимать базовые механики и объяснять их простыми примерами.
  2. Middle: применять тему в продуктовых сценариях с учетом рисков и ограничений.
  3. Senior: управлять архитектурными trade-offs, метриками и эволюцией решения.

Как работать с модулем

  1. Каждый урок проходите в связке: теория -> JS Visual Lab для runtime-механик -> код в консоли/песочнице -> разбор реального кейса.
  2. Для каждой темы фиксируйте два артефакта: краткое объяснение на 40-60 секунд и мини-кейс «где это ломалось/могло сломаться».
  3. После урока закрывайте минимум 3 вопроса из банка по этой теме.

Программа модуля

Урок 1. База языка

Цель: закрыть фундаментальные вопросы, на которых сыпятся даже сильные кандидаты.

Каркас мышления

Перед разбором любого куска JS задавайте 4 вопроса:

  1. Какие здесь типы и как они сравниваются?
  2. В какой области видимости живут переменные?
  3. Какое значение this в точке вызова?
  4. Как будет найдено свойство: own property или по prototype chain?

Типы и сравнения без магии

В JS важно разделять:

  1. Primitive values (string, number, bigint, boolean, null, undefined, symbol).
  2. Objects (включая функции, массивы, даты, map/set).
  3. Reference equality vs value equality.
console.log(0 === false); // false
console.log(0 == false); // true (coercion)

console.log([] === []); // false (разные ссылки)
const a = { x: 1 };
const b = a;
console.log(a === b); // true

Практическое правило: в продуктовой логике избегайте неявных сравнений (==) кроме явно понятных кейсов.

Hoisting, scope, TDZ

var hoist-ится с undefined, let/const hoist-ятся в TDZ.

function demo() {
console.log(a); // undefined
// console.log(b); // ReferenceError
var a = 1;
let b = 2;
}

Прод-следствие: баги из-за случайного var часто выглядят как «иногда undefined в неожиданный момент».

this и prototype chain

this определяется местом вызова, а не местом объявления функции (кроме arrow-function).

const user = {
name: 'Ira',
regular() { return this.name; },
arrow: () => this?.name,
};

console.log(user.regular()); // "Ira"
console.log(user.arrow()); // undefined в strict mode окружении модуля

Prototype chain отвечает за lookup свойств, а не за копирование: это важно при полиморфизме и при дебаге «почему свойство есть в объекте, но не в own keys».

Где ломается в проде

  1. Случайная мутация объекта из shared state.
  2. Неверный this в callback/handler.
  3. Логика на ==, которая проходит тесты и ломается на edge-case данных.

Мини-задача (обязательная)

Подготовьте 5 объяснений по 40-60 секунд: var/let/const, ==/===, null/undefined, this, prototype chain. Для каждого объяснения добавьте один реальный баг-кейс (или правдоподобный сценарий).

Что спросит интервьюер: почему const не делает объект полностью immutable и где это ломает state в UI.

Критерий готовности по уроку: вы можете предсказать поведение 10 коротких фрагментов кода и объяснить каждое поведение через модель языка.

Урок 2. Асинхронность и runtime

Цель: уметь предсказуемо объяснять порядок выполнения и защищаться от async-багов.

Event Loop: минимальная рабочая модель

  1. Сначала выполняется sync-код.
  2. Потом дренируются microtasks (Promise.then, queueMicrotask).
  3. Потом берется следующая macrotask (setTimeout, I/O callbacks).
console.log('A');
setTimeout(() => console.log('B'), 0);
Promise.resolve().then(() => console.log('C'));
console.log('D');
// A, D, C, B

Promise combinators и выбор по задаче

  1. Promise.all - «все или ошибка», быстрый fail.
  2. Promise.allSettled - «получить статус каждого».
  3. Promise.race - «первый завершившийся».
  4. Promise.any - «первый успешный», иначе AggregateError.

Cancellation и stale-response

Одна из самых частых причин «UI показывает старые данные»: старый запрос завершается позже нового и перетирает state.

let currentController;

async function loadUsers(query) {
currentController?.abort();
currentController = new AbortController();

const res = await fetch(`/api/users?q=${encodeURIComponent(query)}`, {
signal: currentController.signal,
});
return res.json();
}

Где ломается в проде

  1. «Проглоченные» ошибки в async-цепочке.
  2. Retry без лимита попыток.
  3. Неправильная обработка abort, когда отмена считается «настоящей ошибкой».

Мини-задача (обязательная)

Разберите 5 фрагментов с setTimeout, Promise, async/await: предскажите порядок логов и объясните, почему именно так. Отдельно реализуйте fetch с timeout + cancel.

Что спросит интервьюер: как вы предотвращаете гонки запросов при быстром переключении фильтров в UI.

Критерий готовности по уроку: вы можете объяснить порядок выполнения кода и показать рабочую защиту от race-condition в UI/сервисе.

Урок 3. Структуры данных и функциональные подходы

Цель: писать предсказуемый и поддерживаемый код для коллекций и трансформаций.

Выбор структуры данных

  1. Array - упорядоченные коллекции, удобно для трансформаций и рендера.
  2. Map - быстрый lookup по ключу и сохранение insertion order.
  3. Set - уникальность значений без ручной дедупликации.
  4. Plain object - хорошо для сериализации, но не всегда лучший выбор для динамического словаря.

Чистые функции и контролируемые side effects

Чистая функция: при одинаковых входах всегда одинаковый выход и нет побочных эффектов. Это резко упрощает тестирование и рефакторинг.

function aggregateOrders(orders) {
return orders.reduce(
(acc, order) => {
const total = order.items.reduce((s, i) => s + i.price * i.qty, 0);
acc.count += 1;
acc.revenue += total;
return acc;
},
{ count: 0, revenue: 0 }
);
}

Иммутабельность: shallow vs deep

{ ...obj } копирует только первый уровень. Если внутри вложенный объект/массив, ссылка сохраняется.

Где это ломается:

  1. Redux-like state обновляется «как будто immutable», но вложенность мутируется.
  2. memo/shouldComponentUpdate не срабатывают корректно.

Readability vs cleverness

reduce полезен, когда реально есть аккумуляция. Если логика читается хуже, выбирайте map + filter и промежуточные имена.

Мини-задача (обязательная)

Реализуйте преобразование списка заказов в агрегированную статистику без мутации входа. Добавьте тест на edge-cases: пустой список, заказ без items, невалидная цена.

Что спросит интервьюер: почему иногда reduce хуже читается, чем комбинация map + filter.

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

Урок 4. Прод-кейсы и дебаг

Цель: перейти от «знаю теорию» к «могу разобрать инцидент».

Runbook: stale data после фильтрации

Пошаговый шаблон:

  1. Зафиксировать сценарий воспроизведения (какие клики, какой интервал, какие фильтры).
  2. Снять network timeline и убедиться, что ответы приходят в другом порядке.
  3. Проверить логику обновления state: есть ли guard по requestId/AbortController.
  4. Проверить, нет ли двойного запроса из-за эффекта/ре-рендера.
  5. Добавить временное логирование requestId -> response -> state update.
  6. Зафиксировать fix и добавить regression test.

Инструменты, которые должны быть «под рукой»

  1. Breakpoints + conditional breakpoints.
  2. Network tab и сортировка по start/end time.
  3. Performance markers (performance.mark/measure) для ключевых операций.
  4. Structured logs в консоли для сложных async-цепочек.

Что отличает инженерный дебаг от хаотичного

  1. У вас есть гипотеза перед каждым действием.
  2. Каждый шаг уменьшает множество возможных причин.
  3. После фикса есть защита от повтора (тест, guard, мониторинг).

Мини-задача (обязательная)

Соберите runbook для бага: «иногда показываются устаревшие данные после фильтрации». Добавьте: признаки бага, шаги проверки, root cause, fix, проверку фикса.

Что спросит интервьюер: как вы доказываете, что проблема именно в async flow, а не в API.

Критерий готовности по уроку: вы можете довести плавающий баг до воспроизводимости, локализовать источник и доказать корректность исправления.

Практика

1. Scope, значения и предсказание выполнения

Связка: Junior JS q-1..q-5, Two Sum, свободная практика в Песочнице.

Что сделать:

  1. Решите Two Sum через const seen = new Map() и let i.
  2. До запуска выпишите результат 5 сниппетов с var, let, const, TDZ, ==, ===, null и undefined.
  3. Напишите isFilled(value) для формы и отдельно объясните поведение 0, '0', false, null, undefined, ''.

Артефакт: короткая таблица case -> expected result -> why -> interview phrase.

Критерий готовности: вы предсказываете выполнение без запуска и можете объяснить, где значение, ссылка или binding ломают ожидание.

2. Functions, objects, this и closure

Связка: Junior JS q-6..q-8, Get By Path.

Что сделать:

  1. Соберите один объект user и сравните method call, detached call, bind и arrow callback.
  2. Реализуйте createCounter() и once(fn), затем докажите, что состояние не лежит в global scope.
  3. Решите Get By Path так, чтобы путь безопасно останавливался на null/undefined.

Артефакт: 3 working snippets с подписью call site -> this/closure/prototype behavior -> risk.

Критерий готовности: вы объясняете this, closure и prototype lookup через конкретный код, а не через определения.

3. Async runtime и stale data

Связка: Junior JS q-9..q-11, Middle q-17..q-18, debug-case из урока про stale data после фильтрации.

Что сделать:

  1. До запуска предскажите порядок 5 сниппетов с sync code, Promise.then, queueMicrotask, setTimeout и await.
  2. Реализуйте fetchWithTimeoutAndAbort(url, ms) с отдельной обработкой success, timeout, abort и network error.
  3. Оформите runbook для бага stale data после фильтрации: признаки, гипотеза, воспроизведение, root cause, fix, проверка фикса.

Артефакт: async trace + runbook, где видно, почему причина именно в гонке/устаревшем response, а не в API.

Критерий готовности: вы объясняете event loop и async bug как цепочку событий во времени и называете защиту от stale response.

4. Collections, immutability и browser boundary

Связка: Junior JS q-12..q-20, Merge Sorted Arrays, Move Zeroes, Group By Field.

Что сделать:

  1. Решите Merge Sorted Arrays и Move Zeroes без мутации входных данных.
  2. Реализуйте groupBy(items, key) и агрегатор заказов с edge-cases: пустой список, неизвестный статус, null-поле, повтор id.
  3. Составьте таблицу data -> storage -> lifetime -> XSS/CSRF risk -> mitigation для theme, draft form, access token и refresh token.

Артефакт: набор функций + ручные проверки ссылок prev === next, edge-case таблица и browser storage risk table.

Критерий готовности: вы выбираете map/filter/reduce/цикл по задаче, доказываете отсутствие мутации и не путаете JS-код с browser security boundary.

Связь с треками и вопросами

  1. Если вы готовитесь на Junior: закройте Junior JS q-1..q-20 и 3 задачи в песочнице.
  2. Если вы уже идете в React/Middle: добавьте Middle q-17..q-18 про async/data flow.
  3. Если нужно подтянуть интервью-формулировку: пройдите Playbook ответов только после того, как код и runbook уже готовы.
  4. Контрольная карта: Event Loop, Closure, this, Prototype, TDZ, Async Race, References и Practice Bridges, строки JS basics, Async runtime, Collections and immutable data, Browser storage and CORS.

Критерий готовности

Ready: вы даете ответ на 1-2 минуты по любой теме модуля в формате модель -> пример кода -> where-it-breaks -> как проверить -> как чинить/предотвращать.

Partial: вы решаете задачи, но объяснение держится на определениях без trace, edge-cases или проверки мутации.

Not ready: вы открываете общий банк вопросов и выбираете случайные темы, не связывая их с конкретным кодом и артефактом.

Артефакты после модуля

  1. Таблица case -> expected result -> why -> interview phrase для scope/equality/TDZ.
  2. 3 working snippets по this, closure и prototype lookup.
  3. fetchWithTimeoutAndAbort + async trace по microtask/macrotask.
  4. Мини-библиотека утилит: debounce, throttle, groupBy, immutable update helper.
  5. Runbook на stale data после фильтрации с доказательством root cause.
  6. Browser storage risk table для токенов, draft form и UI settings.

Куда дальше

  1. React
  2. TypeScript
  3. JavaScript