TLDR
В JavaScript как переменные, так и объявления функций подняты.
Инициализация не.
Подъем означает, что - независимо от лексической позиции объявления - идентификатор семантически присутствует с самого начала области кода (функции или блока).
Это означает Идентификаторы (ie. Имена переменных) семантически присутствуют перед строкой кода, в которой они объявлены, - только для того, чтобы не допустить путаницы между идентификаторами с одинаковыми именами.
Обратите внимание, что поднятый идентификатор может нет (например, если объявлено с использованием let
), доступным для разработчика, пока объявление переменной не будет оценено. ie. через некоторое время после начала выполнения функции или блока. Официальное название для этого периода - «Временная мертвая зона».
В JavaScript подняты как объявления переменных, так и функций.
Инициализация не .
Для переменных, объявленных с var
, эффект состоит в том, что объявление может быть представлено в самом верху включающей функции , независимо от лексического (ie. В коде) позиция объявления.
Для всего остального в коде строгого режима (function
, function*
, let
, const
, class
) эффект состоит в том, что можно представить, что объявления находятся в самом верху вмещающего блока (который может или не может быть функцией), независимо от их лексической позиции объявления.
Не строгий код имеет отдельный, более сложный набор правил для операторов функций, заключенных в блоки. См. также .
В случае переменных, объявленных с использованием var
, сама переменная (со значением по умолчанию undefined
) доступна для присваивания и разыменования (чтения ее значения) из верхней части окружающего контекста выполнения.
Поднятые var
объявления автоматически инициализируются с помощью undefined
.
Для всего остального среда выполнения знает идентификатор из верхней части вмещающего блок, но он не доступен для присваивания или разыменования, пока поток выполнения не перешел точку лексического объявления. ie. что временная мертвая зона прошла.
Поднятые function
/ function*
объявления немедленно инициализируются с поднятой функцией.
Обратите внимание, что в JavaScript алгоритм инициализации контекстов выполнения (стековые фреймы) обрабатывают объявления функций последними, это означает, что объявления function
кажутся поднятыми «выше» объявлений var
.
Это означает, что если объявление функции и var
с одинаковым идентификатором оба объявлены в одной и той же функции, идентификатор будет связан с function
(а не с var
) в начале выполнения включающей функции.
Для let
, const
и class
идентификатор не будет инициализирован до тех пор, пока управление не пройдет мимо лексического объявления переменной.
Эти типы объявлений let
, const
и class
были добавлены к языку намного позже в его жизни (ES 2015).
Разработчики языка выбрали это новое поведение, чтобы сделать JavaScript более легким для понимания и избежать тонких ошибок, которые допускают присваивание и разыменование до того, как может появиться лексическая точка объявления.
По этой причине, В JavaScript был лучший метод, согласно которому указанные переменные должны объявляться в самом верху их окружающих функций.
Так в вашем примере кода:
1 console.log(a);
2 var a = 4;
undefined
undefined
Непосредственно перед выполнение, когда создается экземпляр контекста выполнения (или стекового фрейма) для кода, a
поднимается в верхнюю часть объема.
a
было объявлено с использованием var
, поэтому в строке 1 разрешается разыменование a
внутри console.log(a)
, и автоматически инициализированное значение undefined
выводится на консоль.
В строке 2 код назначит 4
для a
(но ничего не распечатывает).
Если это выполняется в консоли браузера, значение, возвращаемое последним оператором, будет автоматически распечатывается браузером.
В этом случае результат var a = 4;
равен undefined
, поэтому на консоль выводится секунда undefined
.