Объявления переменных являются одним из самых основных аспектов любого языка программирования. Тем не менее, JavaScript имеет небольшую причуду, известную как подъем , которая может превратить невинно выглядящее объявление в небольшую ошибку. В этой статье объясняется, что такое подъем и как его можно избежать.
JavaScript является чрезвычайно гибким языком и, к счастью, позволит вам объявлять переменную практически везде. Например, следующее выражение вызова (IIFE), вызываемое немедленно, объявляет три переменные, а затем отображает их в диалоговом окне предупреждения. Как примечание, вы никогда не должны использовать окна оповещений, но мы пытаемся доказать свою точку зрения здесь.
(function() { var foo = 1; var bar = 2; var baz = 3; alert(foo + " " + bar + " " + baz); })();
Это похоже на здравый код JavaScript. Как и ожидалось, отображается строка "1 2 3"
. Теперь предположим, что предупреждение перемещено, как показано ниже.
(function() { var foo = 1; alert(foo + " " + bar + " " + baz); var bar = 2; var baz = 3; })();
Если кто-то на самом деле написал этот код, возможно, это было по ошибке. Понятно, что предупреждение происходит до того, как объявлены bar
и baz
. Тем не менее, это совершенно правильный JavaScript, который не генерирует исключение. Вместо этого в предупреждении отображается "1 undefined undefined"
.
Исходя из нашего предыдущего эксперимента, кажется, что вы можете ссылаться на переменные, которые еще не существуют. Теперь давайте возьмем тот же IIFE, но полностью удалим объявление baz
, как показано ниже. Внезапно у нас появляется ReferenceError
потому что baz
не определен.
(function() { var foo = 1; alert(foo + " " + bar + " " + baz); var bar = 2; })();
Это действительно интересное поведение. Чтобы понять, что здесь происходит, вы должны понимать подъем. Подъем — это действие интерпретатора JavaScript по перемещению всех объявлений переменных и функций в начало текущей области видимости. Однако, только фактические декларации подняты. Любые задания остаются там, где они есть. Поэтому наш второй пример IIFE фактически переводит в следующий код.
(function() { var foo; var bar; var baz; foo = 1; alert(foo + " " + bar + " " + baz); bar = 2; baz = 3; })();
Теперь понятно, почему второй пример не сгенерировал исключение. После подъема bar
и baz
фактически объявляются перед оператором alert, хотя и с неопределенными значениями. В третьем примере baz
была удалена полностью. Поэтому поднимать было нечего, и аварийное заявление привело к исключению.
Подъем функции
Как упоминалось ранее, объявления функций также поднимаются. Однако функции, которые назначены переменным, не отображаются. Например, следующий код будет работать должным образом из-за подъема объявления функции.
foo(); function foo() { alert("Hello!"); }
Тем не менее, следующий пример не получится эффектно. Объявление переменной для foo
выводится перед вызовом функции. Однако, поскольку присваивание foo
не выполняется, выдается исключение для попытки вызова нефункциональной переменной.
foo(); var foo = function() { alert("Hello!"); };
Вывод
Подъем — это простой для понимания, но часто упускаемый из виду нюанс языка JavaScript. Без должного понимания подъема ваши программы подвержены тонким ошибкам. Чтобы избежать этих ошибок, многие разработчики (и инструменты linting) выступают за использование одного объявления объявления переменной в самом начале каждой области. Поскольку именно так интерпретатор JavaScript и видит ваш код, это правило имеет смысл, даже если я лично виновен в его нарушении.