Статьи

Демистификация JavaScript переменной области и подъем

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

Переменная сфера

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

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

В настоящее время JavaScript, в отличие от многих других языков, не поддерживает область видимости на уровне блоков. Это означает, что объявление переменной внутри блочной структуры, такой как цикл for , не ограничивает эту переменную циклом. Вместо этого переменная будет доступна из всей функции. Стоит отметить, что грядущий ECMAScript 6 будет поддерживать области действия уровня блока через ключевое слово let .

Чтобы прояснить ситуацию, давайте используем простую метафору. У каждой страны в нашем мире есть границы. Все в этих границах принадлежит стране. В каждой стране много городов, и у каждого из них есть свой город. Страны и города похожи на функции JavaScript — у них есть свои локальные возможности. То же самое относится и к континентам. Хотя они огромны по размеру, их также можно определить как локали. С другой стороны, мировые океаны не могут быть определены как имеющие локальную область действия, поскольку они фактически охватывают все локальные объекты — континенты, страны и города — и, таким образом, его область действия определяется как глобальная. Давайте представим это в следующем примере:

 var locales = { europe: function() { // The Europe continent's local scope var myFriend = "Monique"; var france = function() { // The France country's local scope var paris = function() { // The Paris city's local scope console.log(myFriend); }; paris(); }; france(); } }; locales.europe(); 

Попробуйте пример в JS Bin

Теперь, когда мы понимаем, что такое локальные и глобальные области действия и как они создаются, пришло время узнать, как интерпретатор JavaScript использует их для поиска конкретной переменной.

Возвращаясь к данной метафоре, допустим, я хочу найти моего друга по имени Моник. Я знаю, что она живет в Париже, поэтому я начинаю свои поиски оттуда. Когда я не могу найти ее в Париже, я поднимаюсь на один уровень выше и расширяю свои поиски во всей Франции. Но опять же, ее там нет. Затем я снова расширяю свой поиск, поднимаясь на другой уровень. Наконец, я нашел ее в Италии, которая в нашем случае является локальным пространством Европы.

В предыдущем примере мой друг Моник представлен переменной myFriend . В последней строке мы вызываем функцию europe() , которая вызывает france() , и, наконец, когда вызывается функция paris() , начинается поиск. Интерпретатор JavaScript работает из текущей выполняемой области и работает до тех пор, пока не найдет указанную переменную. Если переменная не найдена ни в одной области видимости, возникает исключение.

Этот тип поиска называется лексической (статической) областью действия . Статическая структура программы определяет область видимости переменной. Область видимости переменной определяется ее расположением в исходном коде, а вложенные функции имеют доступ к переменным, объявленным в их внешней области видимости. Независимо от того, откуда вызывается функция или даже как она вызывается, ее лексическая область видимости зависит только от того, где была объявлена ​​функция.

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

Именно этот механизм используется, когда интерпретатор JavaScript пытается найти определенную переменную. Он начинается с самой внутренней области, выполняемой в то время, и продолжается до тех пор, пока не будет найдено первое совпадение, независимо от того, есть ли другие переменные с таким же именем на внешних уровнях или нет. Давайте посмотрим на пример:

 var test = "I'm global"; function testScope() { var test = "I'm local"; console.log (test); } testScope(); // output: I'm local console.log(test); // output: I'm global 

Попробуйте пример в JS Bin

Как мы видим, даже с тем же именем локальная переменная не перезаписывает глобальную переменную после выполнения функции testScope() . Но это не всегда правда. Давайте рассмотрим это:

 var test = "I'm global"; function testScope() { test = "I'm local"; console.log(test); } console.log(test); // output: I'm global testScope(); // output: I'm local console.log(test); // output: I'm local (the global variable is reassigned) 

Попробуйте пример в JS Bin

На этот раз test локальной переменной перезаписывает глобальную переменную с тем же именем. Когда мы запускаем код внутри функции testScope() глобальная переменная переназначается. Если локальная переменная назначается без предварительного объявления с ключевым словом var , она становится глобальной переменной. Чтобы избежать такого нежелательного поведения, вы всегда должны объявлять свои локальные переменные перед их использованием. Любая переменная, объявленная с ключевым словом var внутри функции, является локальной переменной. Рекомендуется объявлять ваши переменные.

Примечание. В строгом режиме это ошибка, если вы назначаете значение переменной без предварительного объявления переменной.

Подъемно

Интерпретатор JavaScript выполняет множество вещей за сценой, и одна из них называется подъемом. Если вы не знаете об этом «скрытом» поведении, это может привести к путанице. Лучший способ думать о поведении переменных JavaScript — это всегда визуализировать их как состоящие из двух частей: объявления и присваивания:

 var state; // variable declaration state = "ready"; // variable definition (assignment) var state = "ready"; // declaration plus definition 

В приведенном выше коде мы сначала объявляем state переменной, а затем присваиваем ей значение «готово». И в последней строке кода мы видим, что эти два шага можно объединить. Но вам нужно иметь в виду, что, хотя на практике они кажутся одним оператором, движок JavaScript рассматривает этот отдельный оператор как два отдельных оператора, как в первых двух строках примера.

Мы уже знаем, что любая переменная, объявленная в области видимости, принадлежит этой области. Но что мы еще не знаем, так это то, что независимо от того, где переменные объявлены в определенной области, все объявления переменных перемещаются в верхнюю часть их области (глобальную или локальную). Это называется подъемом , так как объявления переменных поднимаются в верхнюю часть области видимости. Обратите внимание, что подъем только перемещает объявление. Любые задания остаются на месте. Давайте посмотрим на пример:

 console.log(state); // output: undefined var state = "ready"; 

Попробуйте пример в JS Bin

Как вы можете видеть, когда мы записываем значение state , вывод не undefined , потому что мы ссылаемся на него перед фактическим назначением. Возможно, вы ожидали, что будет сгенерировано ReferenceError, потому что state еще не объявлено. Но чего вы не знаете, так это того, что переменная объявлена ​​за сценой. Вот как код интерпретируется движком JavaScript:

 var state; // moved to the top console.log(state); state = "ready"; // left in place 

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

 function showState() {} // function declaration var showState = function() {}; // function expression 

Самый простой способ отличить объявление функции от выражения функции — это проверить положение function слова в выражении. Если function — это самое первое в утверждении, то это объявление функции. В противном случае это выражение функции.

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

 showState(); // output: Ready function showState() { console.log("Ready"); } var showState = function() { console.log("Idle"); }; 

Попробуйте пример в JS Bin

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

 function showState() { // moved to the top (function declaration) console.log("Ready"); } var showState; // moved to the top (variable declaration) showState(); showState = function() { // left in place (variable assignment) console.log("Idle"); }; 

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

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

 var showState = function() { console.log("Idle"); }; function showState() { console.log("Ready"); } showState(); // output: Idle 

Попробуйте пример в JS Bin

На этот раз мы вызываем showState() в последней строке кода, которая меняет ситуацию. Теперь мы получаем вывод «Idle». Вот как это выглядит при интерпретации движком JavaScript:

 function showState(){ // moved to the top (function declaration) console.log("Ready"); } var showState; // moved to the top (variable declaration) showState = function(){ // left in place (variable assignment) console.log("Idle"); }; showState(); 

То, что нужно запомнить

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