Перейти к содержанию

Переменные и область видимости

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

Будем работать в прежнем app.js. Не нужно одновременно вставлять все альтернативные листинги: повторное объявление одного имени в том же блоке создаёт отдельную ошибку. Если пример назван полным, замените содержимое файла; если он является дополнением, добавьте его в указанное место.

Постоянная связь и изменяемое значение

Рассмотрим полный короткий пример:

const title = 'Современный JavaScript';
const lessons = 20;
let selectedTopic = 'all';
console.log(title, lessons, selectedTopic);
selectedTopic = 'frontend';
console.log(selectedTopic);

Ожидаемое последнее сообщение — frontend. При первом объявлении selectedTopic получает значение all, означающее все темы. Позднее мы присваиваем ему другую строку без повторного let. Объявление создаёт имя в области видимости, присваивание меняет связанную с ним величину.

Если попытаться выполнить lessons = 21, объявление const не разрешит переназначение. Это удобно: случайная замена исходного количества обнаруживается раньше, чем неправильное число попадёт в интерфейс. Однако const не является обещанием глубокой неизменности любых данных. Когда значением станет объект или массив, его внутреннее содержимое сможет меняться; эту разницу отдельно рассмотрим на коллекциях.

Имена выбираем по предметному смыслу. selectedTopic объясняет, что хранится выбранная тема, тогда как x заставляет перечитывать каждое место использования. Значение all является машинным кодом, а не готовой подписью «Все темы». Подпись в дальнейшем будет находиться в HTML формы. Такая организация позволяет менять текст интерфейса без замены условий программы.

Для новых примеров выбирайте const, пока не появится реальная необходимость переназначения. После этого используйте let именно для соответствующего состояния. Это практическая договорённость серии, а не ограничение синтаксиса. В старых материалах часто встречается var; его правила видимости отличаются, поэтому не будем смешивать их с новой моделью без необходимости.

Область блока

Пара фигурных скобок создаёт блок. Для объявлений let и const этот блок ограничивает доступность имени. Проверьте мысленно самостоятельный пример:

const selectedTopic = 'frontend';
{
  const message = `Выбрана тема: ${selectedTopic}`;
  console.log(message);
}
console.log(selectedTopic);

Внутренний блок может читать selectedTopic из внешней области. Переменная message существует только внутри блока. Последняя строка обращается к внешнему имени и поэтому остаётся корректной. Если добавить после закрывающей скобки console.log(message), ожидается ReferenceError: это имя там не определено.

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

Можно объявить во внутреннем блоке другое имя selectedTopic. Тогда внутреннее объявление скроет внешнее до конца блока. Такая возможность иногда полезна, но для учебного каталога лучше назвать промежуточное значение по его роли, например currentTopic. Два одинаковых имени с разными значениями усложняют разбор изменения состояния и легко становятся причиной неверного предположения.

Правила объявлений, видимости и обращения до инициализации изложены в MDN о const и MDN о let.

Обращение до объявления

Имена let и const нельзя использовать до инициализации в их области. Такой участок называют временной мёртвой зоной. Не следует понимать это название как специальный временной таймер: речь идёт о правилах выполнения кода до строки объявления.

console.log(selectedTopic);
let selectedTopic = 'all';

Это намеренно ошибочный полный пример. Ожидается ошибка, а не значение undefined. Чтобы исправить его, перенесите объявление перед чтением. В обычном проекте полезнее выстроить зависимости сверху вниз, чем рассчитывать на особенности подъёма объявлений. Тогда происхождение каждого значения видно рядом с первым использованием.

Отдельная ситуация возникает при let selectedTopic;: объявление выполнено, но начальное значение не задано. После этой строки чтение имени допустимо и даёт undefined. Для состояния формы это обычно плохая отправная точка, потому что дальше придётся угадывать, означает ли отсутствие все темы или ошибку. Лучше задать известное первоначальное состояние all.

Наблюдение в консоли имеет ещё одну особенность: некоторые инструменты позволяют повторно вводить декларации или сохраняют историю между действиями иначе, чем загрузка файла. Когда результат выглядит неожиданно, вернитесь к сохранённому app.js и обновлению документа. Именно этот способ используется в серии для воспроизводимой программы.

Данные и состояние

В дальнейших уроках массив courses будет хранить четыре описания, а отдельный объект selection — параметры отображения. Изменение выбора не должно переписывать название курса или его исходную тему. Мы будем вычислять видимый результат из исходных данных и текущего выбора.

Это решение помогает объяснить, что происходит после возвращения к варианту «Все темы». Если раньше программа уничтожила ненужные записи, восстановить их без повторной загрузки невозможно. Если данные сохранены, меняется только условие отображения. Пока массивов ещё нет, но нужную границу можно увидеть уже на двух отдельных переменных.

Верните полный первый пример, оставив let selectedTopic = 'all', и измените его на frontend после первого сообщения. При каждом обновлении страницы начальное значение снова будет all: память программы не является постоянным хранилищем. Сохранение между посещениями — отдельная задача, которую не будем смешивать с областью видимости.

Повторное объявление имени в той же области отличается от присваивания. Если написать дважды let selectedTopic рядом, программа не получает «новое состояние поверх старого»: разбор обнаружит конфликт имён. Для обновления уже существующего состояния пишите только имя и новое значение. Для самостоятельного отдельного примера очищайте прежнее содержимое файла, как указано перед листингом.

Также не считайте let механизмом сохранения между загрузками страницы. Область определяет доступность имени в одной работающей программе, а продолжительность жизни связана с выполнением и ссылками. Постоянное хранение потребовало бы отдельного API и договорённости о восстановлении. Пока намеренно начинаем каждый запуск с известного выбора всех тем.

Локальное имя удобно ещё и тем, что разные независимые блоки могут использовать одно понятное название промежуточного результата. Однако общие исходные данные не следует заново объявлять в каждом блоке с немного отличающимися значениями. Иначе две части программы будут описывать разные каталоги, хотя читатель увидит одинаковое имя. У данных должен быть один известный источник, у временной подписи — ограниченная область.

Текстовое имя переменной не хранит связь с HTML автоматически. Переменная selectedTopic и поле с name="topic" относятся к разным системам. Позднее обработчик явно прочитает значение поля и присвоит его состоянию. До этого изменение переменной не выберет вариант в интерфейсе, а изменение разметки не переназначит JavaScript-значение само по себе.

Эта граница кажется маленькой в консольном примере, но предотвращает важную ошибку будущего интерфейса: программа должна ясно решить, где находится применённое состояние. Мы не будем угадывать его из текста сообщения или случайного имени узла. Выбор станет отдельным объектом с известным начальным значением и явным обновлением.

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

Оглавление курса