Область видимости и поднятие
var/let/const, поднятие, временная мёртвая зона, лексическая и блочная область видимости, контекст исполнения и затенение переменных.
12 вопросов
JuniorТеорияОчень частоЧто такое поднятие (hoisting) в JavaScript?
Что такое поднятие (hoisting) в JavaScript?
Поднятие — это регистрация объявлений движком до выполнения кода, поэтому имя существует в своей области с самого верха. var поднимается и устанавливается в undefined, поэтому раннее чтение даёт undefined. Объявление функции поднимается целиком и вызываемо до своей строки; let/const поднимаются, но остаются неинициализированными.
Типичные ошибки
- ✗Считать, что поднятие физически перемещает код, а не регистрирует имена заранее
- ✗Ожидать, что чтение
let/constдо их строки дастundefined, как уvar - ✗Думать, что объявления функций нельзя вызвать до их строки
Уточняющие вопросы
- →Почему объявление функции вызываемо до своей строки, а функциональное выражение — нет?
- →Во что вычисляется
var, прочитанный до своего присваивания, и почему?
JuniorТеорияОчень частоВ чём различия между var, let и const?
В чём различия между var, let и const?
var имеет функциональную область, поднимается и инициализируется как undefined, и его можно переобъявлять. let и const блочные, поднимаются, но не инициализируются до своей строки (до неё — временная мёртвая зона), и их нельзя переобъявлять в одной области. const ещё запрещает переприсваивание привязки.
Типичные ошибки
- ✗Думать, что
constделает значение неизменяемым, а не лишь запрещает перепривязку - ✗Считать, что
varсоблюдает блочную область, какlet - ✗Полагать, что
let/constвовсе не поднимаются (они поднимаются, но остаются в TDZ)
Уточняющие вопросы
- →Почему переобъявление
letв двухcaseоператораswitchвыбрасывает ошибку, а в отдельных блоках — нет? - →Что именно замораживает
const— привязку или значение?
JuniorТеорияЧастоЧто такое блочная область видимости и какие объявления её соблюдают?
Что такое блочная область видимости и какие объявления её соблюдают?
Блок — это любая пара {} — if, for или просто скобки. Блочная область означает, что переменная видна лишь внутри ближайшего охватывающего блока. let и const блочные и исчезают за пределами блока. var полностью игнорирует блоки и протекает на всю охватывающую функцию.
Типичные ошибки
- ✗Думать, что
varблочный, какlet/const - ✗Полагать, что скобки
if/forне создают область дляlet/const - ✗Ожидать, что
letиз цикла переживёт его завершение
Уточняющие вопросы
- →Почему
letв заголовкеforсоздаёт свежую привязку на каждой итерации? - →Как блочная область исправляет классический баг замыкания с
varв цикле?
JuniorТеорияЧастоЧто такое функциональная область видимости и как её использует var?
Что такое функциональная область видимости и как её использует var?
Функциональная область означает, что переменная видна везде внутри объявившей её функции, независимо от вложенных блоков. var функционально-областной: var, объявленный внутри любого if или цикла, доступен по всей функции. Каждый вызов функции создаёт свежую область для своих локальных переменных.
Типичные ошибки
- ✗Думать, что каждый вызов переиспользует одну общую область, а не свежую
- ✗Считать, что
letфункционально-областной, какvar - ✗Ожидать, что
varвнутри блока ограничен этим блоком
Уточняющие вопросы
- →Как свежая область на каждый вызов делает возможными рекурсию и замыкания?
- →Почему две вложенные функции могут обе объявить
var xбез конфликта?
MiddleТеорияЧастоЧто такое лексическая область видимости и как цепочка областей разрешает имя?
Что такое лексическая область видимости и как цепочка областей разрешает имя?
Лексическая область означает, что область имени определяется тем, где написан код, а не где он выполняется. Вложенная функция может читать переменные охватывающих функций. Для разрешения имени движок идёт по цепочке областей наружу — локальная, затем каждая охватывающая, затем глобальная — останавливаясь на первом совпадении.
Типичные ошибки
- ✗Путать лексическую область с динамической (по месту вызова)
- ✗Думать, что цепочка областей ищется от глобальной внутрь, а не от локальной наружу
- ✗Считать, что функция достаёт лишь свою непосредственную родительскую область
Уточняющие вопросы
- →Как замыкания опираются на лексическую область для захвата переменных?
- →Почему затенение останавливает поиск по цепочке областей на внутренней привязке?
MiddleТеорияЧастоЧто такое временная мёртвая зона (TDZ) в JavaScript?
Что такое временная мёртвая зона (TDZ) в JavaScript?
Временная мёртвая зона — это промежуток между входом в область и строкой, инициализирующей let или const. Имя поднято и существует в области, но чтение или запись во время TDZ выбрасывают ReferenceError. У var нет TDZ — до своей строки он читается как undefined.
Типичные ошибки
- ✗Думать, что у
varесть TDZ (он вместо этого читается какundefined) - ✗Считать, что имя не существует в области во время TDZ
- ✗Ожидать, что доступ в TDZ вернёт
undefined, а не выбросит ошибку
Уточняющие вопросы
- →Почему
typeof x— этоReferenceErrorдляletв его TDZ, но безопасен для необъявленного имени? - →Как TDZ помогает ловить баги использования до объявления?
JuniorТеорияИногдаЧто такое глобальные переменные и как создать одну случайно?
Что такое глобальные переменные и как создать одну случайно?
Глобальная переменная живёт в области верхнего уровня и доступна из любого места программы. Объявление на верхнем уровне создаёт её намеренно; присваивание имени без var/let/const создаёт её случайно — но лишь в нестрогом режиме, тогда как строгий режим вместо этого выбрасывает ReferenceError.
Типичные ошибки
- ✗Полагать, что присваивание без объявления создаёт глобал даже в строгом режиме
- ✗Думать, что
varвнутри функции протекает в глобальную область - ✗Считать глобалы безвредными, а не источником конфликтов имён
Уточняющие вопросы
- →Почему строгий режим превращает случайный глобал в
ReferenceError? - →Какие проблемы создают общие глобальные переменные в большой кодовой базе?
MiddleТеорияИногдаЧто такое контекст исполнения и каковы две его фазы?
Что такое контекст исполнения и каковы две его фазы?
Контекст исполнения — это среда, в которой выполняется код, хранящая его переменные, цепочку областей и this. Он проходит две фазы: фаза создания настраивает привязки и поднимает объявления; фаза исполнения выполняет код построчно. Существует один глобальный контекст плюс по одному на каждый вызов функции.
Типичные ошибки
- ✗Думать, что на всю программу один контекст исполнения
- ✗Переворачивать порядок фаз создания и исполнения
- ✗Смешивать контекст исполнения со стеком вызовов
Уточняющие вопросы
- →Как фаза создания объясняет, почему
varрано читается какundefined? - →Что кладётся и снимается со стека вызовов по мере появления и исчезновения контекстов?
MiddleТеорияИногдаЧто такое затенение переменных и недопустимое затенение?
Что такое затенение переменных и недопустимое затенение?
Затенение — это переменная внутренней области, повторяющая внешнее имя; во внутренней области побеждает внутренняя привязка, а внешняя скрыта. Недопустимое затенение — SyntaxError, когда var сталкивается с let/const в пересекающейся области, ведь var протекает из блока и конфликтует с блочным именем.
Типичные ошибки
- ✗Думать, что обычное затенение само по себе ошибка
- ✗Считать, что внешняя привязка побеждает во внутренней области
- ✗Переворачивать недопустимый случай — это
varповерхlet/const, а неletповерхvar
Уточняющие вопросы
- →Почему
let a; { var a; }выбрасывает ошибку, аvar a; { let a; }— нет? - →Как затенение взаимодействует с поиском по цепочке областей?
MiddleТеорияИногдаВ чём разница между необъявленной и неопределённой (undefined) переменной?
В чём разница между необъявленной и неопределённой (undefined) переменной?
Неопределённая переменная объявлена, но не присвоена, поэтому её чтение возвращает undefined. Необъявленная переменная нигде в цепочке областей не объявлена, поэтому её чтение выбрасывает ReferenceError (x is not defined). Первое — обычное значение; второе — провал поиска имени.
Типичные ошибки
- ✗Думать, что чтение необъявленной переменной возвращает
undefined, а не выбрасывает ошибку - ✗Полагать, что оба случая выбрасывают одну ошибку
- ✗Путать значение
undefinedс проваленным поиском имени
Уточняющие вопросы
- →Почему
typeof undeclaredVarвозвращаетundefined, а не выбрасывает ошибку? - →Как поднятие объясняет, почему раннее чтение
varдаётundefined?
SeniorТеорияРедкоКак фаза создания настраивает поднятие, TDZ и цепочку областей?
Как фаза создания настраивает поднятие, TDZ и цепочку областей?
В фазе создания движок строит запись окружения контекста: привязывает каждый var к undefined, привязывает объявления функций к их функциям и привязывает имена let/const, оставляя их неинициализированными — источник TDZ. Он также связывает внешнее окружение, формируя цепочку областей. Фаза исполнения затем присваивает реальные значения.
Типичные ошибки
- ✗Думать, что фаза создания присваивает итоговые значения, а не заглушки
- ✗Считать, что цепочка областей строится при вызове, а не из лексической вложенности
- ✗Считать TDZ учебной метафорой, а не неинициализированной привязкой
Уточняющие вопросы
- →Почему объявления функций пригодны до своей строки, а функциональные выражения — нет?
- →Чем запись окружения отличается между глобальным и функциональным контекстом?
SeniorТеорияРедкоПочему JavaScript лексически областной и как это разрешает затенённые имена?
Почему JavaScript лексически областной и как это разрешает затенённые имена?
JavaScript лексически областной: свободные переменные функции разрешаются по областям, охватывающим её в исходнике, и это фиксируется при определении функции, а не при вызове. Имя ищется наружу по этой лексической цепочке, и первая (самая внутренняя) найденная привязка затеняет одноимённую внешнюю. Это же позволяет замыканию сохранять область своего определения живой.
Типичные ошибки
- ✗Путать лексическую область JavaScript с динамической (по вызывающему)
- ✗Думать, что поиск стартует с глобальной и спускается, а не с локальной и поднимается
- ✗Считать, что замыкания захватывают глобальную область, а не область определения
Уточняющие вопросы
- →Как динамическая область изменила бы то, какую переменную захватывает замыкание?
- →Почему два замыкания из одной фабрики могут разделять или изолировать состояние?