Что такое поднятие hoisting

Поднятие в JavaScript

Хочешь проверить свои знания по JS?

Подпишись на наш канал с тестами по JS в Telegram!

Поднятие (англ. hoisting) в JavaScript позволяет использовать функции и переменные до их объявления. В этой статье мы разберем, что собой представляет поднятие и как оно работает.

Что такое поднятие в JavaScript?

Взгляните на этот код и попробуйте угадать, что произойдет при его запуске:

Дело в том, что интерпретатор JavaScript разделяет объявление и назначение значений для функций и переменных. Ваши объявления «поднимаются» вверх их области видимости перед выполнением.

Этот процесс называется поднятием. Именно благодаря ему мы можем использовать переменную foo в нашем примере еще до того, как она объявлена.

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

Поднятие переменных в JavaScript

Чисто в качестве напоминания: мы объявляем переменные при помощи операторов var, let и const. Например:

Мы назначаем значение переменной, используя оператор присваивания:

Во многих случаях объявление и присваивание значения комбинируются в один шаг:

Поднятие переменных, объявленных при помощи var

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

Использовав необъявленную переменную до присвоения ей значения, вы также получите ошибку ReferenceError, потому что здесь нет объявления, которое могло бы подняться:

Теперь вы, вероятно, думаете: «Хм. Это как-то странно, что JavaScript позволяет получить доступ к переменным до того как они объявлены». Это поведение — необычная часть JavaScript. Оно может привести к ошибкам, поэтому использование переменной до ее объявления обычно нежелательно.

Поднятие переменных, объявленных при помощи let и const

Обратите внимание, что интерпретатор по-прежнему поднимает foo: сообщение об ошибке говорит нам, что где-то эта переменная инициализирована.

Временная мертвая зона

TDZ начинается в начале области видимости переменной и заканчивается ее объявлением. Обращение к переменной в TDZ приводит к выбросу ReferenceError.

Вот пример понятного блока, показывающего начало и конец временной мертвой зоны foo:

TDZ также присутствует в дефолтных параметрах функции, которые оцениваются слева направо. В следующем примере bar находится в TDZ, пока не получает значение по умолчанию:

Но этот код работает, потому что мы можем обратиться к foo вне ее TDZ:

typeof во временной мертвой зоне

Это поведение согласуется с другими примерами с let и const в TDZ, которые мы уже видели. Здесь мы получаем ReferenceError, потому что foo объявлена, но не инициализирована. Мы должны знать, что используем ее до инициализации.

Кроме того (и это удивительно), мы можем проверить тип несуществующей переменной и не получить ошибку. typeof безопасно возвращает строку:

Фактически, появление let и const сломало гарантию того, что typeof всегда возвращает строковое значение для любого операнда.

Поднятие функций в JavaScript

Объявления функций тоже поднимаются. Это позволяет нам вызывать функцию до ее объявления. Например, следующий код успешно запускается и выдает результат «foo»:

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

Если мы попытаемся вызвать переменную, которой было присвоено в качестве значения функциональное выражение, мы получим TypeError или ReferenceError — в зависимости от области видимости переменной:

Это отличается от вызова вообще не объявленной функции, который вызвал бы другую ошибку — ReferenceError:

Как использовать поднятие в JavaScript

Поднятие переменных

Если вы работаете над старой кодовой базой или вам еще по какой-то причине приходится использовать var, MDN рекомендует писать var-объявления как можно выше в области видимости. Таким образом скоуп ваших переменных будет яснее.

Вы также можете подумать над использованием правила no-use-before-define в ESLint, чтобы точно не использовать переменные до их объявления.

Поднятие функций

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

Вот вам надуманный пример:

Взглянув на этот код, мы сразу получаем представление о том, что он делает, и нам для этого не приходится сначала прочесть все объявления функций.

Тем не менее, использование функций до их объявления — дело вкуса. Некоторые разработчики, например Wes Bos, предпочитают другой путь. Они помещают функции в модули, которые можно импортировать при необходимости. (Источник: Wes Bos).

Руководство по стилю от Airbnb идет еще дальше. Чтобы предотвратить обращение до объявления, гайд советует отдавать предпочтение именованным функциональным выражениям, а не объявлениям:

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

Если вы полагаете, что определение функции настолько большое и сложное, что помешает читателю понять остальной файл, возможно, пора выделить это определение в модуль!»

Заключение

Спасибо за внимание! Надеюсь, эта статья помогла вам познакомиться с темой поднятия в JavaScript.

Источник

Поднятие в 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

Что такое поднятие hoisting. Смотреть фото Что такое поднятие hoisting. Смотреть картинку Что такое поднятие hoisting. Картинка про Что такое поднятие hoisting. Фото Что такое поднятие hoisting

И как пользоваться этим “поднятием”

Возможно, вы уже знаете, что переменные могут “подниматься”. “Hoisting” переводится с английского как “поднятие” и означает понятие, которое было придумано для того, чтобы можно было говорить о замыканиях в JavaScript без указания области видимости переменных.

Перед тем как начать, следует ознакомиться с терминами из статьи, такими как лексическое окружение, обработчики синтаксиса и контексты выполнения.

Что такое поднятие hoisting. Смотреть фото Что такое поднятие hoisting. Смотреть картинку Что такое поднятие hoisting. Картинка про Что такое поднятие hoisting. Фото Что такое поднятие hoisting

Теперь рассмотрим то, что, скорее всего, вообще не будет работать в других языках программирования. Вернемся к коду: передвинем вызов функции b() и вывод значения переменной а вверх, в начало кода.

Что такое поднятие hoisting. Смотреть фото Что такое поднятие hoisting. Смотреть картинку Что такое поднятие hoisting. Картинка про Что такое поднятие hoisting. Фото Что такое поднятие hoisting

В большинстве языков программирования такая запись выдаст ошибку, поскольку обычно они выполняют код строка за строкой. Так как функция b() еще не была объявлена перед вызовом, мы пока не можем ее использовать. По крайней мере такого поведения следует ожидать. Однако в JavaScript дела обстоят немного иначе.

Консоль выдает ошибку a is not defined (переменная а не определена).

Теперь помещаем переменную внутрь JS-файла.

Такой феномен называется “поднятием” (hoisting).

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

Что такое поднятие hoisting. Смотреть фото Что такое поднятие hoisting. Смотреть картинку Что такое поднятие hoisting. Картинка про Что такое поднятие hoisting. Фото Что такое поднятие hoisting

Происходит так, будто мы объявили переменную, а значение будет присвоено ей позднее. Но это не то, что было написано. Дело в том, что весь код преобразуется движком JavaScript.

Вот код, который мы писали в начале.

Что такое поднятие hoisting. Смотреть фото Что такое поднятие hoisting. Смотреть картинку Что такое поднятие hoisting. Картинка про Что такое поднятие hoisting. Фото Что такое поднятие hoisting

Чтобы разобраться во внутренних процессах JavaScript, нужно копнуть немного глубже в контекст выполнения программы. Дело в том, что он запускается в два этапа. Это и есть причина, по которой переменные и функции JavaScript в некотором роде доступны, даже если были объявлены в коде позже.

Что такое поднятие hoisting. Смотреть фото Что такое поднятие hoisting. Смотреть картинку Что такое поднятие hoisting. Картинка про Что такое поднятие hoisting. Фото Что такое поднятие hoisting

Следует помнить, что this создается внутри контекста выполнения программы. Затем создаётся внешнее окружение.

Что такое поднятие hoisting. Смотреть фото Что такое поднятие hoisting. Смотреть картинку Что такое поднятие hoisting. Картинка про Что такое поднятие hoisting. Фото Что такое поднятие hoisting

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

Но JavaScript не перемещает код вверх. На самом деле его движок выделяет место в памяти для всех переменных, лежащих в коде, еще до начала построчного выполнения программы.

Когда код начинает запускаться строка за строкой, он уже имеет доступ ко всем элементам. Однако в случае переменных все немного сложнее. Функции, будучи обработанными парсером, целиком помещаются в память. Вторая фаза (фаза выполнения, когда код выполняется построчно) — это как раз тот момент, когда настраиваются все присваивания, а переменная а получает какое-либо значение.

Все эти процессы происходят, потому что где-то в лексическом окружении языка происходит нечто, представленное ниже.

Это значит, что опираться на поднятие переменных и функций — не лучшая идея, оно может доставить кучу проблем.

Вот как делать не нужно.

Вместо этого лучше сделать так.

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

Сравнение var, let и const при поднятии

Как вы думаете, каким будет результат вывода программы?

Что такое поднятие hoisting. Смотреть фото Что такое поднятие hoisting. Смотреть картинку Что такое поднятие hoisting. Картинка про Что такое поднятие hoisting. Фото Что такое поднятие hoisting

Эта ошибка всплывает из-за Временной мертвой зоны (Temporal Dead Zone), однако не стоит пугаться этого термина. Он обозначает период между созданием переменной и её инициализацией, когда мы не можем получить к ней доступ.

Значит ли это, что все переменные, объявленные с помощью let и const не “поднимаются” в коде? Нет, они тоже поднимаются.

Источник

Разбираемся с “поднятием” (hoisting) в JavaScript

Что такое поднятие hoisting. Смотреть фото Что такое поднятие hoisting. Смотреть картинку Что такое поднятие hoisting. Картинка про Что такое поднятие hoisting. Фото Что такое поднятие hoisting

Feb 23, 2018 · 9 min read

Что такое поднятие hoisting. Смотреть фото Что такое поднятие hoisting. Смотреть картинку Что такое поднятие hoisting. Картинка про Что такое поднятие hoisting. Фото Что такое поднятие hoisting

👉 Мой Твиттер — там много из мира фронтенда, да и вообще поговорим🖖. Подписывайтесь, будет интересно: ) ✈️

Что такое поднятие hoisting. Смотреть фото Что такое поднятие hoisting. Смотреть картинку Что такое поднятие hoisting. Картинка про Что такое поднятие hoisting. Фото Что такое поднятие hoisting

В этом руководстве вы изучите то, как срабатывает всеми извезтный механизм “ поднятия” в JavaScript. Ну или в оригинальном названии hoisting. Однако, перед тем как углубиться в детали, давайте узнаем что это вообще такое и как оно работает на самом деле.

Поднятие или hoisting — это механизм в JavaScript, в котором переменные и объявления функций, передвигаются вверх своей области видимости перед тем, как код будет выполнен.

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

Стоит отметить то, что механизм “ поднятия” передвигает только объявления функции или переменной. Назначения переменным остаются на своих местах.

В общем, если вы когда-либо удивлялись, почему вы могли вызывать функции перед тем, как они написаны в коде, то читайте дальше.

Undefined vs ReferenceError

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

Вторым заключением будет:

В JavaScript, ReferenceError появляется при попытке доступа к предварительно необъявленной переменной.

Поведение JavaScript при работе с переменными становится довольно утонченным делом из-за «поднятия». Мы увидим это более детально в следующих параграфах.

«Поднятие» переменных

Ниже вы видите цикл работы JavaScript, показывающий последовательность, в которой происходит объявление и инициализация переменных.

Что такое поднятие hoisting. Смотреть фото Что такое поднятие hoisting. Смотреть картинку Что такое поднятие hoisting. Картинка про Что такое поднятие hoisting. Фото Что такое поднятие hoisting

Однако, в 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 и «поднятие» переменных и объявлений функций

Вы знаете, какое значение выведет этот код на JavaScript?

Если вас удивляет, что выведется «10», то следующий код вас и вовсе запутает:

В этом случае браузер выведет «1». Так что, собственно, происходит? Хотя такое поведение кажется странным, опасным и сбивающим с толку, на самом деле это очень мощное и выразительное средство JavaScript. Я не знаю, есть ли официальное название для такого поведения, но мне нравится использовать термин «поднятие»(«hoisting»). В этой статье я попытаюсь пролить свет на этот механизм языка, но сначала давайте поговорим об области видимости в JavaScript.

Область видимости в JavaScript

Одна из причин, приводящих в замешательство новичков, — это область видимости. Вообще, не только новичков. Я встречал много опытных JavaScript-разработчиков, которые не понимают механизм области видимости в JavaScript. Причина в том, что внешне JavaScript очень похож на любой другой Си-подобный язык.
Давайте рассмотрим следующий код на Cи:

Эта программа выведет 1, 2, 1, потому что Си и все остальные Си-подобные языки реализуют области видимости на уровне блоков кода. Когда исполняется новый блок кода, например условие if, новые переменные, объявленные в нём, не повлияют на переменные внешней области видимости.
Но не в случае JavaScript. Попробуйте запустить вот этот код в Firebug:

На этот раз будут выведены числа 1, 2, 2. Это связано с тем, что в JavaScript используется область видимости на уровне функций. Это совсем не то, что мы привыкли видеть в языках программирования, вроде Си. Блоки кода, вроде того, который у нас идёт сразу после if, не создают новую область видимости. Только функции создают новые области видимости.
Для многих программистов, привыкших к Си, C++, C# или Java такое поведение очень неожиданное и неприятное. К счастью, благодаря гибкости функций JavaScript, можно обойти эту проблему. Чтобы создать временную область видимости внутри функции, достаточно сделать следующее:

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

Объявления, именование и «поднятие» переменных и функций

на самом деле интерпретируется так:

Оказывается, не важно, будет ли вообще выполнена строка, в которой происходит объявление. Следующие две функции эквивалентны:

Обратите внимание, что присваивание значений переменным не поднимается вместе с их объявлением. Поднимаются только объявления переменных. В случае с функциями, поднимается вся функция целиком. Существуют два основных способа объявить функцию, давайте их рассмотрим:

в этом случае поднимается только функция bar. Идентификатор «foo» также поднимается, но не анонимная функция — она остаётся на месте.

Вот мы и описали основные моменты «поднятия» переменных и функций. Конечно, JavaScript не был бы сам собой, если бы не было особых случаев, в которых всё немного сложнее.

Разрешение имён

Именованные функциональные выражения

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

Как писать код, обладая такими знаниями

Итак, теперь вы понимаете область видимости и «поднятие» переменных и объявлений функций. Что это означает применительно к написанию кода на JavaScript? Самое главное — всегда объявлять ваши переменные, используя var. Я настаиваю на том, чтобы у вас был ровно один var на область видимости и чтобы он располагался в её начале. Если вы заставите себя так делать, у вас никогда не будет проблем, связанных с «поднятием». Тем не менее, это может привести к тому, что сложно следить за переменными, которые объявлены в текущей области видимости. Я рекомендую использовать JSLint с включённой опцией onevar, чтобы вынудить вас так делать. Если вы будете так всё делать, ваш код будет выглядеть примерно так:

Что говорит стандарт

Если инструкция переменной встречается внутри ОбъявленияФункции, переменные объявляются внутри локальной области видимости для данной функции согласно описанию в разделе 10.1.3. В противном случае они объявляются в глобальной области видимости (т.е. создаются как поля глобального объекта согласно описанию в разделе 10.1.3) с использованием атрибутов свойств < DontDelete >. Переменные создаются, когда происходит вход в область выполнения. Блок не определяет новой области выполнения. Только Программа и ОбъявлениеФункции создают новую область видимости. Переменные инициализируются при создании значением undefined. Переменной, для которой определён Инициализатор, присваивается значение его ВыраженияПрисваивания в момент выполнения ИнструкцииПеременной, а не в момент создания переменной.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *