Базовый CS с нуля
Variables and state: построй state-контейнер
Читать про aliasing — не то же самое, что наблюдать, как твоя собственная история снимков молча переписывает себя, потому что два имени разделяли один объект. Построй небольшой state-контейнер, записывай снимки его состояния во времени, затем сломай его нарочно багом aliasing и почини — доказывая каждый шаг трассировкой того, что лежит в ячейках.
Преврати модель памяти юнита в работающий код: представь состояние программы именованными переменными, мутируй его контролируемыми присваиваниями, снимай честные снимки и продемонстрируй на практике разницу между разделением reference и копированием значения.
Построй крошечный in-memory контейнер «игрового состояния» на TypeScript с историей снимков, затем намеренно внеси баг aliasing, портящий историю, диагностируй его из первых принципов и почини явной копией — задокументировав причину каждого поведения на уровне ячеек.
- Запуск баговой версии выводит history, где каждый снимок идентичен финальному состоянию, а твой разбор называет причиной разделение reference — а не логическую ошибку в applyMove.
- Запуск починенной версии выводит history из различных снимков, каждый соответствует состоянию в момент захвата, причём вложенный inventory тоже независим в каждом снимке.
- Краткое письменное объяснение различает, со ссылкой на реальный код, где значение было скопировано, а где reference был разделён, и почему const на состоянии не предотвратил mutation.
- Лог мутаций ясно показывает состояние как кумулятивный итог отдельных мутаций, а не один скачок.
- Добавь undo(), восстанавливающий предыдущий снимок. Проверь, что он НЕ возвращает aliasing: восстановление не должно заставить живое состояние разделять reference с хранимой записью history, иначе поздняя mutation снова испортит историю.
- Замени ручные копии { ...state } на structuredClone и объясни, как это меняет поведение вложенного inventory по сравнению с shallow spread.
- Добавь путь freeze() через Object.freeze на каждый снимок и покажи, что случайная mutation хранимого снимка теперь падает (или молча игнорируется в non-strict) — делая неизменность истории явной, а не договорной.
- Напиши self-quiz из 6 вопросов (с ответами), который поймал бы коллегу, всё ещё верящего, что 'let copy = original' дублирует объект.
Это цикл за огромным классом реальных багов: состояние — это просто именованные ячейки, которые ты мутируешь во времени, а «снимок» честен, только если копирует значения, а не разделяет reference. Сохранение reference в список history делает его алиасом живого объекта, поэтому каждая поздняя mutation переписывает прошлое; копирование значения в момент захвата это чинит, а shallow-копия всё ещё оставляет вложенные объекты разделяемыми. Сделав это раз на игрушечном контейнере, ты делаешь production-версию — редьюсеры Redux, audit-логи, отслеживание изменений — очевидной, а не загадочной.