Область видимости и поднятие
var/let/const, поднятие, временная мёртвая зона, лексическая и блочная область видимости, контекст исполнения и затенение переменных.
16 вопросов
JuniorТеорияОчень частоЧто такое поднятие (hoisting) в JavaScript?
Что такое поднятие (hoisting) в JavaScript?
Поднятие — это регистрация объявлений движком до выполнения кода, поэтому имя существует в своей области с самого верха. var поднимается и устанавливается в undefined, поэтому раннее чтение даёт undefined. Объявление функции поднимается целиком и вызываемо до своей строки; let/const поднимаются, но остаются неинициализированными.
Типичные ошибки
- ✗Считать, что поднятие физически перемещает код, а не регистрирует имена заранее
- ✗Ожидать, что чтение
let/constдо их строки дастundefined, как уvar - ✗Думать, что объявления функций нельзя вызвать до их строки
Уточняющие вопросы
- →Почему объявление функции вызываемо до своей строки, а функциональное выражение — нет?
- →Во что вычисляется
var, прочитанный до своего присваивания, и почему?
JuniorТеорияОчень частоВ чём различия между var, let и const?
В чём различия между var, let и const?
var принадлежит всей функции и не замечает блоков {} внутри неё, а let и const существуют только внутри блока, где объявлены. Кроме того, const запрещает потом присвоить переменной новое значение.
Типичные ошибки
- ✗Ждать, что
var, объявленный внутри блокаifилиfor, останется там, какlet:varне замечает блоков и виден во всей функции. - ✗Ждать, что
constпримет новое значение позже: повторное присваивание останавливает программу сTypeError: Assignment to constant variable.
Уточняющие вопросы
- →Что напечатает
console.log(x)строкой вышеvar x = 5;в той же функции? - →Можно ли делать
pushв массив, объявленный черезconst?
JuniorТеорияЧастоЧто такое блочная область видимости и какие объявления её соблюдают?
Что такое блочная область видимости и какие объявления её соблюдают?
Блок — это любая пара {} — if, for или просто скобки. Блочная область означает, что переменная видна лишь внутри ближайшего охватывающего блока. let и const блочные и исчезают за пределами блока. var полностью игнорирует блоки и протекает на всю охватывающую функцию.
Типичные ошибки
- ✗Думать, что
varблочный, какlet/const - ✗Полагать, что скобки
if/forне создают область дляlet/const - ✗Ожидать, что
letиз цикла переживёт его завершение
Уточняющие вопросы
- →Почему
letв заголовкеforсоздаёт свежую привязку на каждой итерации? - →Как блочная область исправляет классический баг замыкания с
varв цикле?
JuniorТеорияЧастоЧто произойдёт при запуске программы?
Что произойдёт при запуске программы?
1, затем TypeError
Открыть задачу →Типичные ошибки
- ✗Ждать
1, затем 2: константеconstнельзя дать новое значение. Строкаx = 2останавливает программу сTypeError: Assignment to constant variable.; для меняющейся переменной естьlet. - ✗Ждать только
TypeError: присваивание падает, лишь когда выполняется его строка, а строка 2 к этому моменту уже вывела1.
Уточняющие вопросы
- →Как изменить программу, чтобы она вывела
1 2без ошибки? - →Что выведет программа, если удалить строку 3?
JuniorТеорияЧастоЧто такое функциональная область видимости и как её использует var?
Что такое функциональная область видимости и как её использует var?
Функциональная область означает, что переменная видна везде внутри объявившей её функции, независимо от вложенных блоков. var функционально-областной: var, объявленный внутри любого if или цикла, доступен по всей функции. Каждый вызов функции создаёт свежую область для своих локальных переменных.
Типичные ошибки
- ✗Думать, что каждый вызов переиспользует одну общую область, а не свежую
- ✗Считать, что
letфункционально-областной, какvar - ✗Ожидать, что
varвнутри блока ограничен этим блоком
Уточняющие вопросы
- →Как свежая область на каждый вызов делает возможными рекурсию и замыкания?
- →Почему две вложенные функции могут обе объявить
var xбез конфликта?
JuniorТеорияЧастоЧто выведет программа?
Что выведет программа?
3 3 3
Открыть задачу →Типичные ошибки
- ✗Ждать
0 1 2: сvarпеременнаяiодна на весь цикл, а функции читают её при вызове, уже после цикла; своюiна каждый проход даёт толькоlet. - ✗Ждать
2 2 2: цикл останавливается, только когдаi < 3ложно, поэтому после последнего проходаi++делает её равной3.
Уточняющие вопросы
- →Что выведет программа, если заменить
varнаlet? - →Что напечатает
console.log(i)сразу после цикла?
JuniorТеорияЧастоЧто выведет программа (строки через пробел)?
Что выведет программа (строки через пробел)?
2 1
Открыть задачу →Типичные ошибки
- ✗Ждать
2 2, будто внутренняя строка изменила внешнийx:let x = 2объявляет новую переменную, которая живёт только внутри{}и закрывает там внешнюю. - ✗Ждать
SyntaxErrorиз-за второго объявленияx: эта ошибка бывает только в одном и том же блоке, аletво вложенном блоке — отдельная переменная.
Уточняющие вопросы
- →Что напечатает программа, если внутренняя строка —
x = 2безlet? - →Как коду внутри блока добраться до внешнего
x, когда тот закрыт?
JuniorТеорияЧастоЧто выведет программа?
Что выведет программа?
undefined
Открыть задачу →Типичные ошибки
- ✗Ждать
ReferenceError, как сlet: переменнаяvarсуществует с первой строки функции и до строкиvar x = 5хранитundefined. - ✗Ждать
5, будто вся строка переехала наверх: заранее появляется только переменная, а значение5присваивается, когда выполняется её строка.
Уточняющие вопросы
- →Что сделает программа, если заменить
var x = 5наlet x = 5? - →Можно ли вызвать функцию, объявленную через
function, строкой выше её объявления?
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 с динамической (по вызывающему)
- ✗Думать, что поиск стартует с глобальной и спускается, а не с локальной и поднимается
- ✗Считать, что замыкания захватывают глобальную область, а не область определения
Уточняющие вопросы
- →Как динамическая область изменила бы то, какую переменную захватывает замыкание?
- →Почему два замыкания из одной фабрики могут разделять или изолировать состояние?