Что в javascript подвергается всплытию hoisting
Поднятие в JS (Hoisting в Javascript + 3 примера)
В этой статье мы с вами разберемся как что такое, и как работает поднятие (Hoisting) в Javascript. Эта одна из базовых тем в ванильном JS и вы однозначно наткнетесь на нее в одном из интервью при устройстве на работу.
Поднятие в JS (пример с функцией)
Для нашего примера создадим функцию letsGo и попробуем ее вызвать до ее создания.
Наша функция запускается, и мы получаем строку «Go!!» в нашей консоли. Это происходит, так как срабатывает мехнаизм «поднятие» в Javascript.
То есть, «под капотом» компилятор JS «поднимает» все строчки, где объявляются функции на самый верх.
Ваглядит это так:
Теперь, давайте немного расширим наш пример и создадим еще одну функцию add.
Мы видим, что наша вторая функция add также срабатывает и получаем результат сложения в консоли. То есть, опять, сработал механизм «поднятия» в JS, который поднял весь код, где объявляются функции на самый верх.
Применительно к функциям, «поднятие» работает только с объявлением функций!
Поднятие в JS не работает при использовании функциональных выражений, стрелочных функций и любых других способов создания функций.
То есть, если мы попробуем использовать функциональное выражение и запустить функцию до ее создания, то получим ошибку:
Если решим написать то же самое, используя стрелочную функцию, тоже получим ошибку («поднятие» не работает):
Поднятие в Javascript (пример с переменной var)
Давайте, ради интереса, также выведем в лог произвольную переменную, которой у нас вообще не существует:
То есть, когда мы выводим в лог значение переменной years, до ее создания, происходит следующее:
Обработчики Событий в JS (как работает addEventListener)
Замыкания в Javascript (что такое замыкание в JS + 3 примера)
Разбираемся с “поднятием” (hoisting) в JavaScript
Feb 23, 2018 · 9 min read
👉 Мой Твиттер — там много из мира фронтенда, да и вообще поговорим🖖. Подписывайтесь, будет интересно: ) ✈️
В этом руководстве вы изучите то, как срабатывает всеми извезтный механизм “ поднятия” в JavaScript. Ну или в оригинальном названии hoisting. Однако, перед тем как углубиться в детали, давайте узнаем что это вообще такое и как оно работает на самом деле.
Поднятие или hoisting — это механизм в JavaScript, в котором переменные и объявления функций, передвигаются вверх своей области видимости перед тем, как код будет выполнен.
Как следств и е, это означает то, что совершенно неважно где были объявлены функция или переменные, все они передвигаются вверх своей области видимости, вне зависимости от того локальная она или же глобальная.
Стоит отметить то, что механизм “ поднятия” передвигает только объявления функции или переменной. Назначения переменным остаются на своих местах.
В общем, если вы когда-либо удивлялись, почему вы могли вызывать функции перед тем, как они написаны в коде, то читайте дальше.
Undefined vs ReferenceError
Перед тем, как мы начнем серьёзно углубляться в этот вопрос, давайте проясним несколько вещей.
Вторым заключением будет:
В JavaScript, ReferenceError появляется при попытке доступа к предварительно необъявленной переменной.
Поведение JavaScript при работе с переменными становится довольно утонченным делом из-за «поднятия». Мы увидим это более детально в следующих параграфах.
«Поднятие» переменных
Ниже вы видите цикл работы JavaScript, показывающий последовательность, в которой происходит объявление и инициализация переменных.
Однако, в JavaScript мы можем объявлять и инициализировать наши переменные одновременно, как в этом ну просто самом распространенном примере:
Запомните и держите в уме одну важную деталь, JavaScript непреклонно сначала объявляет, а уже затем инициализирует наши переменные.
Как упоминалось ранее, все переменные и объявления функций поднимаются вверх своей области видимости. Мне также стоит добавить, что объявление переменных происходит перед выполнением кода.
Но однако, необъявленные переменные не существуют до тех пор, пока код назначающий их не будет выполнен. Следовательно, указание значения для необъявленной переменной, тут же создаёт её как глобальную переменную, когда назначение будет выполнено. Это говорит о том, что все необъявленные переменные это по факту глобальные переменные.
Чтобы продемонстрировать это поведение, давайте посмотрим на следующий код.
Так как это одна из причуд работы JavaScript с переменными, рекомендуется всегда объявлять их, вне зависимости от их положения в коде, в функции они или в глобальной области видимости.
Это ясно указывает на то, как движок JavaScript должен с ними работать во время выполнения кода.
Глобальные переменные
Почему так произошло?
JavaScript «поднял» объявление переменной. Вот как это выглядит для движка JavaScript:
Переменные в области видимости функции
Вот как движок видит код выше:
Чтобы избегать таких ловушек, нам всегда нужно убеждаться в том, что мы объявляем и инициализируем переменные перед их использованием.
Strict Mode или «Строгий режим»
Устраняет некоторые скрытые ошибки в JavaScript, изменяя их на явную выдачу ошибок, которые будут в последствии выданы движком.
Устраняет ошибки, которые затрудняют движкам JavaScript выполнять оптимизацию.
Запрещает некоторый синтаксис, который с большой долей вероятности будет уже идти из коробки в будущих версиях JavaScript.
Мы включаем « строгий режим», заранее указывая в нашем файле или функции следующее:
Тем не менее, use-strict ведет себя по разному в разных браузерах, так что будет вполне благоразумно провести тестирование функционала перед использованием в работе.
Тут нам представляет интерес то, как этот стандарт влияет на объявление и инициализацию JavaScript переменных.
Перед тем как идти дальше, стоит отметить то, что переменные объявленные через let заключены в область видимости блока, а не функции. Это очень важно, но это не должно нас сейчас волновать.
Вкратце, это просто говорит о том, что область видимости переменной привязана к блоку, в котором она объявлена, а не к функции в которой она объявлена.
Это еще раз доказывает то, что надо сначала объявлять наши переменные.
Следовательно, чтобы не напороться на предыдущие ошибки, нам нужно сначала объявить переменную, а потом назначить ей значение и только потом уже её использовать.
const была представлена в es6 для того, чтобы можно было сделать неизменные переменные. Да, именно это, переменные значение которых не может быть изменено или переназначено.
Давайте посмотрим, что происходит если мы попытаемся переназначить значение, прикрепленное к const переменной.
Как const изменяет объявление переменной? Давайте посмотрим.
Тоже самое происходит при использовании const в функциях.
Наш линтер быстренько информирует нас об этом просчете:
Следовательно, константные переменные должны быть объявлены и инициализированы перед использованием.
Поднятие функций
JavaScript функции могут классифицироваться как объявленные функции, так и как функциональные выражения. Далее мы узнаем как «поднятие» влияет на оба типа.
Такие функции полностью поднимаются вверх кода. Теперь понятно почему JavaScript позволяет нам вызывать функции прежде, чем мы их объявим по упоминанию в коде.
Функциональные выражения, однако, не поднимаются.
Как мы можем видеть выше, объявление переменной var expression поднимается, но его назначение как функции — нет. Следовательно, движок выдаст TypeError как увидит expression в виде переменой, а не функции.
Порядок по приоритетам
Очень важно помнить несколько вещей, объявляя JavaScript функции и переменные.
Назначение переменных имеет приоритет перед объявлением функции.
Объявление функции имеет приоритет перед объявлением переменной.
Объявления функций «поднимаются» над объявлением переменных, но не над их назначениями.
Давайте посмотрим как это работает.
Назначение переменной над объявлением функции.
Объявление функции над объявлением переменной.
Даже если мы изменим позиции объявлений, JavaScript движок все равно бы взял double функцию.
«Поднятие» классов
Классы в JavaScript могут также классифицироваться как объявления и выражения.
Так же как и свои коллеги функции, JavaScript классы при объявлении « поднимаются». Тем не менее, они остаются неинициализированными до определения. Это говорит о том, что вам надо объявить класс перед тем, как его использовать.
Если мы взглянем на линтер, то там мы увидим полезный совет.
Поэтому, чтобы получить доступ к классу, вам нужно сначала его объявить.
Так же как и свои коллеги по функциям, выражения классов не « поднимаются».
Вот пример анонимного варианта выражения класса.
А вот пример с названным выражением класса.
А вот правильный порядок:
Предостережение
Заключение
Давайте подведем итоги того, что мы изучили:
1. Нам нужно взять в привычку, объявлять и инициализировать JavaScript переменные перед использованием.
2. Использование strict mode в JavaScript es5 может помочь выявить необъявленные переменные.
Что в javascript подвергается всплытию hoisting
Разбираемся с “поднятием” (hoisting) в JavaScript
В этом руководстве вы изучите то, как срабатывает всеми извезтный механизм “поднятия” в JavaScript. Ну или в оригинальном названии hoisting. Однако, перед тем как углубиться в детали, давайте узнаем что это вообще такое и как оно работает на самом деле.
Поднятие или hoisting — это механизм в JavaScript, в котором переменные и объявления функций, передвигаются вверх своей области видимости перед тем, как код будет выполнен.
Как следств и е, это означает то, что совершенно неважно где были объявлены функция или переменные, все они передвигаются вверх своей области видимости, вне зависимости от того локальная она или же глобальная.
Стоит отметить то, что механизм “поднятия” передвигает только объявления функции или переменной. Назначения переменным остаются на своих местах.
В общем, если вы когда-либо удивлялись, почему вы могли вызывать функции перед тем, как они написаны в коде, то читайте дальше.
Undefined vs ReferenceError
Перед тем, как мы начнем серьёзно углубляться в этот вопрос, давайте проясним несколько вещей.
Вторым заключением будет:
В JavaScript, ReferenceError появляется при попытке доступа к предварительно необъявленной переменной.
Поведение JavaScript при работе с переменными становится довольно утонченным делом из-за «поднятия». Мы увидим это более детально в следующих параграфах.
«Поднятие» переменных
Ниже вы видите цикл работы JavaScript, показывающий последовательность, в которой происходит объявление и инициализация переменных.
Однако, в JavaScript мы можем объявлять и инициализировать наши переменные одновременно, как в этом ну просто самом распространенном примере:
Запомните и держите в уме одну важную деталь, JavaScript непреклонно сначала объявляет, а уже затем инициализирует наши переменные.
Как упоминалось ранее, все переменные и объявления функций поднимаются вверх своей области видимости. Мне также стоит добавить, что объявление переменных происходит перед выполнением кода.
Но однако, необъявленные переменные не существуют до тех пор, пока код назначающий их не будет выполнен. Следовательно, указание значения для необъявленной переменной, тут же создаёт её как глобальную переменную, когда назначение будет выполнено. Это говорит о том, что все необъявленные переменные это по факту глобальные переменные.
Чтобы продемонстрировать это поведение, давайте посмотрим на следующий код.
Так как это одна из причуд работы JavaScript с переменными, рекомендуется всегда объявлять их, вне зависимости от их положения в коде, в функции они или в глобальной области видимости.
Это ясно указывает на то, как движок JavaScript должен с ними работать во время выполнения кода.
ES5
Глобальные переменные
Почему так произошло?
JavaScript «поднял» объявление переменной. Вот как это выглядит для движка JavaScript:
Переменные в области видимости функции
Вот как движок видит код выше:
Чтобы избегать таких ловушек, нам всегда нужно убеждаться в том, что мы объявляем и инициализируем переменные перед их использованием.
Strict Mode или «Строгий режим»
Запуск кода в strict mode :
Устраняет некоторые скрытые ошибки в JavaScript, изменяя их на явную выдачу ошибок, которые будут в последствии выданы движком.
Устраняет ошибки, которые затрудняют движкам JavaScript выполнять оптимизацию.
Запрещает некоторый синтаксис, который с большой долей вероятности будет уже идти из коробки в будущих версиях JavaScript.
Мы включаем «строгий режим», заранее указывая в нашем файле или функции следующее:
Тем не менее, use-strict ведет себя по разному в разных браузерах, так что будет вполне благоразумно провести тестирование функционала перед использованием в работе.
ES6
Тут нам представляет интерес то, как этот стандарт влияет на объявление и инициализацию JavaScript переменных.
Перед тем как идти дальше, стоит отметить то, что переменные объявленные через let заключены в область видимости блока, а не функции. Это очень важно, но это не должно нас сейчас волновать.
Вкратце, это просто говорит о том, что область видимости переменной привязана к блоку, в котором она объявлена, а не к функции в которой она объявлена.
Это еще раз доказывает то, что надо сначала объявлять наши переменные.
Следовательно, чтобы не напороться на предыдущие ошибки, нам нужно сначала объявить переменную, а потом назначить ей значение и только потом уже её использовать.
const
const была представлена в es6 для того, чтобы можно было сделать неизменные переменные. Да, именно это, переменные значение которых не может быть изменено или переназначено.
Давайте посмотрим, что происходит если мы попытаемся переназначить значение, прикрепленное к const переменной.
Как const изменяет объявление переменной? Давайте посмотрим.
Тоже самое происходит при использовании const в функциях.
Наш линтер быстренько информирует нас об этом просчете:
Следовательно, константные переменные должны быть объявлены и инициализированы перед использованием.
Поднятие функций
JavaScript функции могут классифицироваться как объявленные функции, так и как функциональные выражения. Далее мы узнаем как «поднятие» влияет на оба типа.
Объявленные функции
Такие функции полностью поднимаются вверх кода. Теперь понятно почему JavaScript позволяет нам вызывать функции прежде, чем мы их объявим по упоминанию в коде.
Функциональные выражения, однако, не поднимаются.
Как мы можем видеть выше, объявление переменной var expression поднимается, но его назначение как функции — нет. Следовательно, движок выдаст TypeError как увидит expression в виде переменой, а не функции.
Порядок по приоритетам
Очень важно помнить несколько вещей, объявляя JavaScript функции и переменные.
Назначение переменных имеет приоритет перед объявлением функции.
Объявление функции имеет приоритет перед объявлением переменной.
Объявления функций «поднимаются» над объявлением переменных, но не над их назначениями.
Давайте посмотрим как это работает.
Назначение переменной над объявлением функции.
Объявление функции над объявлением переменной.
Даже если мы изменим позиции объявлений, JavaScript движок все равно бы взял double функцию.
«Поднятие» классов
Классы в JavaScript могут также классифицироваться как объявления и выражения.
Объявления классов
Так же как и свои коллеги функции, JavaScript классы при объявлении «поднимаются». Тем не менее, они остаются неинициализированными до определения. Это говорит о том, что вам надо объявить класс перед тем, как его использовать.
Если мы взглянем на линтер, то там мы увидим полезный совет.
Поэтому, чтобы получить доступ к классу, вам нужно сначала его объявить.
Выражения классов
Так же как и свои коллеги по функциям, выражения классов не «поднимаются».
Вот пример анонимного варианта выражения класса.
А вот пример с названным выражением класса.
А вот правильный порядок:
Предостережение
Заключение
Давайте подведем итоги того, что мы изучили:
1. Нам нужно взять в привычку, объявлять и инициализировать JavaScript переменные перед использованием.
2. Использование strict mode в JavaScript es5 может помочь выявить необъявленные переменные.
Что такое Hoisting в JavaScript
Возможно, вы уже знаете, что переменные могут “подниматься”. “Hoisting” переводится с английского как “поднятие” и означает понятие, которое было придумано для того, чтобы можно было говорить о замыканиях в JavaScript без указания области видимости переменных.
Перед тем как начать, следует ознакомиться с терминами из статьи, такими как лексическое окружение, обработчики синтаксиса и контексты выполнения.
Результат совпадает с ожиданиями
Теперь рассмотрим то, что, скорее всего, вообще не будет работать в других языках программирования. Вернемся к коду: передвинем вызов функции b() и вывод значения переменной а вверх, в начало кода.
В большинстве языков программирования такая запись выдаст ошибку, поскольку обычно они выполняют код строка за строкой. Так как функция b() еще не была объявлена перед вызовом, мы пока не можем ее использовать. По крайней мере такого поведения следует ожидать. Однако в JavaScript дела обстоят немного иначе.
Функция вызвалась несмотря на то, что была объявлена ниже
А что будет, если вызвать переменную а без объявления?
Консоль выдает ошибку a is not defined (переменная а не определена).
Как и следовало ожидать
Теперь помещаем переменную внутрь JS-файла.
Объявим переменную а где-нибудь после ее вызова
Вывод выдаст результат undefined
Такой феномен называется “поднятием” (hoisting).
Описания в интернете могут дать неверное представление об этом процессе. Как правило, в них говорится о том, что переменные и функции в JavaScript поднимаются в самый верх программы из-за движка JS, будто их на самом деле туда переместили, а поэтому они могут работать в любом месте.
В результате мы все равно получим undefined, как на фото ниже
Происходит так, будто мы объявили переменную, а значение будет присвоено ей позднее. Но это не то, что было написано. Дело в том, что весь код преобразуется движком JavaScript.
Вот код, который мы писали в начале.
Чтобы разобраться во внутренних процессах JavaScript, нужно копнуть немного глубже в контекст выполнения программы. Дело в том, что он запускается в два этапа. Это и есть причина, по которой переменные и функции JavaScript в некотором роде доступны, даже если были объявлены в коде позже.
Следует помнить, что this создается внутри контекста выполнения программы. Затем создаётся внешнее окружение.
В фазе создания парсер проходит через весь код и начинает настраивать написанное для преобразования. Он распознает места, где мы создали переменные или функции, а затем выделяет пространство в памяти для всех этих данных. И именно этот процесс называют поднятием.
Но JavaScript не перемещает код вверх. На самом деле его движок выделяет место в памяти для всех переменных, лежащих в коде, еще до начала построчного выполнения программы.
Когда код начинает запускаться строка за строкой, он уже имеет доступ ко всем элементам. Однако в случае переменных все немного сложнее. Функции, будучи обработанными парсером, целиком помещаются в память. Вторая фаза (фаза выполнения, когда код выполняется построчно) — это как раз тот момент, когда настраиваются все присваивания, а переменная а получает какое-либо значение.
Все переменные в JavaScript по умолчанию заданы ключевым словом undefined
Все эти процессы происходят, потому что где-то в лексическом окружении языка происходит нечто, представленное ниже.
Вот что происходит “под капотом” у JavaScript
Это значит, что опираться на поднятие переменных и функций — не лучшая идея, оно может доставить кучу проблем.
Вот как делать не нужно.
Не вызывайте переменные и функции до того, как объявите их
Вместо этого лучше сделать так.
Сначала объявите переменные, а затем вызывайте их
Теперь мы уже понимаем, что значит поднятие. Мы и вправду можем вызвать функцию несмотря на то, что она объявлена позже. Это связано с тем, что написанный код не выполняется напрямую. Движок JS обрабатывает его и лишь затем принимает решения. Это немного странно, но так он работает.
Сравнение var, let и const при поднятии
Переменная а объявлена ключевым словом let
Как вы думаете, каким будет результат вывода программы?
Эта ошибка всплывает из-за Временной мертвой зоны (Temporal Dead Zone), однако не стоит пугаться этого термина. Он обозначает период между созданием переменной и её инициализацией, когда мы не можем получить к ней доступ.
Значит ли это, что все переменные, объявленные с помощью let и const не “поднимаются” в коде? Нет, они тоже поднимаются.
Поднятие
Поднятие (hoisting) — термин, который вы не встретите в документации JavaScript. Поднятие задумывалось как общий способ мышления о том, как контекст исполнения (в частности, фазы создания и исполнения) работает в JavaScript. Однако, hoisting может привести и к недоразумениям. Например, hoisting учит, что объявление переменной или функции физически перемещается в начало вашего кода, хотя в действительности этого не происходит. На самом же деле, объявления переменных и функций попадают в память в процессе фазы компиляции, но остаются в коде на том месте, где вы их объявили.
Узнаем больше
Пример:
Одним из преимуществ помещения в память объявлений функций до выполнения кода то, что можно использовать функцию до её объявления. Например:
Предыдущий пример демонстрирует ожидаемый рабочий код. Теперь давайте посмотрим, что происходит, когда мы вызываем функцию в коде до её объявления:
Даже если мы вызываем функцию до её объявления, код работает. Это происходит благодаря тому, как работает контекст выполнения в JavaScript.
Hoisting хорошо работает и с другими типами данных и переменными. Переменные могут быть инициализированы и использованы до их объявления. Однако, они не могут быть использованы без инициализации.