Какова область видимости переменных в JavaScript?

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

1.02.2009 08:27:19
Вот еще одна приятная ссылка, чтобы помнить об этой проблеме: « Объяснение области видимости и замыканий JavaScript ».
Full-Stack Software Engineer 28.08.2012 22:25:09
Вот статья, которая объясняет это очень хорошо. Все, что вам нужно знать о области видимости переменной Javascript
Saurab Parakh 25.03.2014 09:55:06
Упоминался ранее электронная книга Кайл Симпсона доступна для чтения на Github, и он говорит вам все , что вам нужно знать о JavaScript Скоупсе и колпачках. Вы можете найти его здесь: github.com/getify/You-Dont-Know-JS/blob/master/… Это часть серии книг «Вы не знаете JS» , которая отлично подходит для всех, кто хотел бы знать больше о JavaScript.
3rik82 7.02.2017 10:21:44
25 ОТВЕТОВ
РЕШЕНИЕ

TLDR

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

Четыре области применения:

  1. Глобальный - виден всем
  2. Функция - видимая внутри функции (и ее подфункций и блоков)
  3. Блок - видимый внутри блока (и его подблоков)
  4. Модуль - видимый внутри модуля

Вне особых случаев глобальной и модульной области, переменные объявляются с использованием var(область действия функции), let(область действия блока) и const(область действия блока). Большинство других форм объявления идентификаторов имеют строгую область видимости блока.

обзор

Область действия - это область кодовой базы, по которой действителен идентификатор.

Лексическая среда - это отображение между именами идентификаторов и значениями, связанными с ними.

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

Эти связанные лексические среды образуют «цепочку». Разрешение идентификатора - это процесс поиска в этой цепочке соответствующего идентификатора.

Разрешение идентификатора происходит только в одном направлении: наружу. Таким образом, внешние лексические среды не могут «заглянуть» во внутренние лексические среды.

Есть три соответствующие факторы в определении сферы в качестве идентификатора в JavaScript:

  1. Как был объявлен идентификатор
  2. Где идентификатор был объявлен
  3. Находитесь ли вы в строгом режиме или не строгом режиме

Некоторые из способов идентификации идентификаторов:

  1. var, letиconst
  2. Параметры функции
  3. Параметр блока захвата
  4. Объявления функций
  5. Выражения именованных функций
  6. Неявно определенные свойства глобального объекта (т. Е. Пропущенные varв нестрогом режиме)
  7. import заявления
  8. eval

Некоторые из идентификаторов местоположений могут быть объявлены:

  1. Глобальный контекст
  2. Тело функции
  3. Обычный блок
  4. Вершина управляющей структуры (например, цикл, если, в то время как и т. Д.)
  5. Орган управления корпусом
  6. Модули

Декларация Стили

вар

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

пусть и const

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

Примечание: let, constи var все поднимают . Это означает, что их логическая позиция определения является вершиной их охватывающей области (блока или функции). Тем не менее, переменные объявлены с использованием letи constне могут быть прочитаны или назначены, пока контроль не пройдет точку объявления в исходном коде. Промежуточный период известен как временная мертвая зона.

function f() {
    function g() {
        console.log(x)
    }
    let x = 1
    g()
}
f() // 1 because x is hoisted even though declared with `let`!

Имена параметров функции

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

Объявления функций

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

Выражения именованных функций

Выражения именованных функций ограничены самими собой (например, с целью рекурсии).

Неявно определенные свойства глобального объекта

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

Eval

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

Примеры

Следующее вызовет ReferenceError, потому что имена x, yи не zимеют никакого значения вне функции f.

function f() {
    var x = 1
    let y = 1
    const z = 1
}
console.log(typeof x) // undefined (because var has function scope!)
console.log(typeof y) // undefined (because the body of the function is a block)
console.log(typeof z) // undefined (because the body of the function is a block)

Следующее вызовет ReferenceError для yи z, но не для x, потому что видимость блока xне ограничена. Блоки , которые определяют органы управления структур , таких как if, forи whileведут себя аналогичным образом .

{
    var x = 1
    let y = 1
    const z = 1
}
console.log(x) // 1
console.log(typeof y) // undefined because `y` has block scope
console.log(typeof z) // undefined because `z` has block scope

Далее xвидно за пределами цикла, поскольку varимеет область действия функции:

for(var x = 0; x < 5; ++x) {}
console.log(x) // 5 (note this is outside the loop!)

... из-за этого поведения вы должны быть осторожны при закрытии переменных, объявленных varв циклах. Здесь объявлен только один экземпляр переменной x, и он логически находится вне цикла.

Следующая распечатка выполняется 5пять раз, а затем распечатывается 5в шестой раз за console.logпределами цикла:

for(var x = 0; x < 5; ++x) {
    setTimeout(() => console.log(x)) // closes over the `x` which is logically positioned at the top of the enclosing scope, above the loop
}
console.log(x) // note: visible outside the loop

Следующие распечатки, undefinedпотому что xэто блок-область. Обратные вызовы запускаются один за другим асинхронно. Новое поведение для letпеременных означает, что каждая анонимная функция закрывается на другую переменную с именем x(в отличие от того, что было бы сделано с var), и, таким образом, выводятся целые числа 0через . 4:

for(let x = 0; x < 5; ++x) {
    setTimeout(() => console.log(x)) // `let` declarations are re-declared on a per-iteration basis, so the closures capture different variables
}
console.log(typeof x) // undefined

Следующее НЕ выдает a, ReferenceErrorпотому что видимость xне ограничена блоком; однако он напечатает, undefinedпотому что переменная не была инициализирована (из-за ifоператора).

if(false) {
    var x = 1
}
console.log(x) // here, `x` has been declared, but not initialised

Переменная, объявленная в верхней части forцикла при помощи, letпопадает в тело цикла:

for(let x = 0; x < 10; ++x) {} 
console.log(typeof x) // undefined, because `x` is block-scoped

Следующее будет выбрасывать, ReferenceErrorпотому что видимость xограничена блоком:

if(false) {
    let x = 1
}
console.log(typeof x) // undefined, because `x` is block-scoped

Переменные , объявленные с использованием var, letили constвсе области видимости модулей:

// module1.js

var x = 0
export function f() {}

//module2.js

import f from 'module1.js'

console.log(x) // throws ReferenceError

Следующее объявит свойство для глобального объекта, потому что переменные, объявленные с использованием varв глобальном контексте, добавляются как свойства к глобальному объекту:

var x = 1
console.log(window.hasOwnProperty('x')) // true

letи constв глобальном контексте не добавляйте свойства к глобальному объекту, но все же имейте глобальную область видимости:

let x = 1
console.log(window.hasOwnProperty('x')) // false

Параметры функции можно считать объявленными в теле функции:

function f(x) {}
console.log(typeof x) // undefined, because `x` is scoped to the function

Параметры блока захвата ограничены телом блока захвата:

try {} catch(e) {}
console.log(typeof e) // undefined, because `e` is scoped to the catch block

Выражения с именованными функциями относятся только к самому выражению:

(function foo() { console.log(foo) })()
console.log(typeof foo) // undefined, because `foo` is scoped to its own expression

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

x = 1 // implicitly defined property on the global object (no "var"!)

console.log(x) // 1
console.log(window.hasOwnProperty('x')) // true

В нестрогом режиме объявления функций имеют область действия. В строгом режиме они имеют блочную область видимости.

'use strict'
{
    function foo() {}
}
console.log(typeof foo) // undefined, because `foo` is block-scoped

Как это работает под капотом

Область действия определяется как лексическая область кода, по которой действителен идентификатор.

В JavaScript, каждая функция-объект имеет скрытую [[Environment]]ссылку , которая является ссылкой на лексическую среду в контексте выполнения (кадре стеки) , в течение которого он был создан.

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

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

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

Узнайте более .

2518
14.04.2020 18:59:16
Даже близко не быть исчерпывающим, но это, возможно, необходимый набор приемов Javascript, которые необходимы, чтобы эффективно ПРОЧИТАТЬ современный Javascript.
Triptych 1.02.2009 09:08:41
Высоко оцененный ответ, не уверен почему. Это просто набор примеров без надлежащего объяснения, а затем, кажется, путает наследование прототипа (то есть разрешение свойства) с цепочкой области видимости (то есть разрешение переменной). Полное (и точное) объяснение области видимости и разрешения свойств содержится в примечаниях к часто задаваемым вопросам comp.lang.javascript .
RobG 10.09.2012 00:35:18
@RobG Его высоко ценят, потому что он полезен и понятен широкому кругу программистов, несмотря на незначительный катахрез. Ссылка, которую вы разместили, хотя и полезна для некоторых специалистов, для большинства людей, пишущих сегодня Javascript, непонятна. Не стесняйтесь исправлять любые проблемы с номенклатурой, редактируя ответ.
Triptych 10.09.2012 05:34:39
@ триптих - я только редактирую ответы, чтобы исправить мелкие вещи, а не основные. Изменение «области действия» на «свойство» исправит ошибку, но не проблему смешивания наследования и области без очень четкого различия.
RobG 10.09.2012 07:02:36
Если вы определяете переменную во внешней области видимости, а затем используете оператор if, определите переменную внутри функции с тем же именем, даже если это, если ветвь не достигнута, она будет переопределена. Пример - jsfiddle.net/3CxVm
Chris S 12.06.2013 23:28:45

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

(Я уверен, что есть много тонкостей, на которые настоящие программисты JavaScript смогут указать в других ответах. В частности, я наткнулся на эту страницу о том, что именно thisозначает в любое время. Надеюсь, этой более вводной ссылки достаточно, чтобы вы начали, хотя .)

108
25.02.2014 18:12:12
Я боюсь даже начать отвечать на этот вопрос. Как настоящий программист Javascript, я знаю, как быстро ответ может выйти из-под контроля. Хорошие статьи.
Triptych 1.02.2009 08:33:47
@Triptych: Я знаю, что ты имеешь в виду, когда дела выходят из-под контроля, но все равно , пожалуйста, добавь ответ. Я получил вышеупомянутое, просто сделав пару поисков ... ответ, написанный кем-то с реальным опытом, обязательно будет лучше. Пожалуйста, исправьте любой мой ответ, который, безусловно, неверен!
Jon Skeet 1.02.2009 08:37:54
Почему-то Джон Скит отвечает за МОЙ самый популярный ответ о переполнении стека.
Triptych 9.01.2018 16:29:49

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

Элемент в цепочке областей действия - это в основном Map с указателем на родительскую область видимости.

При разрешении переменной javascript начинается с самой внутренней области и выполняет поиск за ее пределами.

233
14.08.2012 07:54:06
Цепочки контекста - это еще один термин для замыканий [памяти] ... для тех, кто читает здесь, чтобы узнать / войти в javascript.
New Alexandria 5.02.2014 20:17:17

Вот пример:

<script>

var globalVariable = 7; //==window.globalVariable

function aGlobal( param ) { //==window.aGlobal(); 
                            //param is only accessible in this function
  var scopedToFunction = {
    //can't be accessed outside of this function

    nested : 3 //accessible by: scopedToFunction.nested
  };

  anotherGlobal = {
    //global because there's no `var`
  }; 

}

</script>

Вы захотите исследовать замыкания и узнать, как использовать их для создания частных пользователей .

39
1.02.2009 08:48:09

В «Javascript 1.7» (расширение Mozilla до Javascript) также можно объявить переменные области блока с помощью letоператора :

 var a = 4;
 let (a = 3) {
   alert(a); // 3
 }
 alert(a);   // 4
26
6.04.2010 11:19:39
Да, но безопасно ли это использовать? Я имею в виду, я бы реально выбрал эту реализацию, если мой код будет работать в WebKit?
IgorGanapolsky 28.12.2010 17:39:14
@Python: Нет, WebKit не поддерживает let.
kennytm 28.12.2010 18:29:11
Полагаю, единственное правильное использование этого было бы, если бы вы знали, что все клиенты будут использовать браузер Mozilla, как для внутренней системы компании.
GazB 11.10.2012 08:06:17
Или, если вы программируете с использованием фреймворка XUL, фреймворка интерфейса Mozilla, где вы создаете с использованием css, xml и javascript.
Gerard ONeill 25.10.2013 00:02:20
@GazB даже это ужасная идея! Итак, сегодня вы знаете, что ваши клиенты используют Mozilla, а затем выходит новое сообщение о том, что теперь они используют что-то другое. То есть причина, по которой наша система оплаты - отстой ... Вы должны использовать IE8, а не IE9, IE10, Firefox или Chrome, потому что это не сработает ...
buzzsawddog 22.11.2013 17:49:42

Ключ, насколько я понимаю, состоит в том, что Javascript имеет определение уровня функции по сравнению с более распространенной областью видимости блока C.

Вот хорошая статья на эту тему.

31
15.05.2012 17:38:30

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

Попробуйте функцию на:

Посмотреть демо на:

Посмотреть код на:

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

9
21.03.2013 17:31:11
У меня не работает Firefox 26. Я вставляю код или загружаю файл, нажимаю «Выполнить», и ничего не происходит.
mplwork 13.02.2014 18:44:30
Сфера и наследование две разные вещи.
Ben Aston 27.02.2020 17:53:11

Попробуйте этот любопытный пример. В приведенном ниже примере, если бы a было числовым значением, инициализированным в 0, вы бы увидели 0, а затем 1. За исключением того, что a является объектом, и javascript передаст f1 указатель, а не его копию. В результате вы получаете одно и то же предупреждение оба раза.

var a = new Date();
function f1(b)
{
    b.setDate(b.getDate()+1);
    alert(b.getDate());
}
f1(a);
alert(a.getDate());
3
10.08.2013 17:37:14

1) Существует глобальная область действия, область действия функции, а также области действия with и catch. В общем случае для переменных нет области уровня «блок» - операторы with и catch добавляют имена в свои блоки.

2) Области применения вложены функциями вплоть до глобальной области видимости.

3) Свойства определяются путем прохождения цепочки прототипов. Оператор with переводит имена свойств объекта в лексическую область, определенную блоком with.

РЕДАКТИРОВАТЬ: ECMAAScript 6 (Harmony) предназначен для поддержки let, и я знаю, что Chrome позволяет флаг 'Harmony', поэтому, возможно, он поддерживает его ..

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

РЕДАКТИРОВАТЬ: Исходя из того, что Бенджамин указал на утверждения with и catch в комментариях, я отредактировал пост и добавил больше. Оба оператора with и catch вводят переменные в соответствующие блоки, и это является областью видимости блоков. Эти переменные связываются со свойствами переданных в них объектов.

 //chrome (v8)

 var a = { 'test1':'test1val' }
 test1   // error not defined
 with (a) { var test1 = 'replaced' }
 test1   // undefined
 a       // a.test1 = 'replaced'

РЕДАКТИРОВАТЬ: Уточняющий пример:

test1 ограничен блоком with, но имеет псевдоним a.test1. «Var test1» создает новую переменную test1 в верхнем лексическом контексте (функция или глобальный), если только она не является свойством a, которым она является.

Хлоп! Будьте осторожны, используя 'with' - точно так же, как var - это noop, если переменная уже определена в функции, это также noop относительно имен, импортируемых из объекта! Небольшое упоминание имени, которое уже определено, сделает это намного безопаснее. Я лично никогда не буду использовать с из-за этого.

17
6.06.2014 17:13:06
У вас здесь есть некоторые ошибки, потому что в одном JavaScript есть формы блочной видимости.
Benjamin Gruenbaum 25.10.2013 00:55:37
Мои уши (глаза) открыты, Бенджамин. Мои высказанные выше заявления относятся к тому, как я лечу Javascript, но они не основаны на чтении спецификации. И я надеюсь, что вы не ссылаетесь на оператор with (который является формой видимости объекта) или специальный синтаксис Mozilla 'let'.
Gerard ONeill 25.10.2013 14:02:07
Хорошо, withоператор является формой видимости блока, но catchпредложения - гораздо более распространенная форма (забавный факт, v8 реализует catchс a with) - это практически единственные формы видимости блока в самом JavaScript (то есть функция, глобальная, try / catch , с и их производными), однако хост-среды имеют разные понятия области видимости - например, встроенные события в браузере и модуль VM NodeJS.
Benjamin Gruenbaum 25.10.2013 16:42:18
Бенджамин - из того, что я вижу, и с, и с помощью catch только вводят объект в текущую область (и, следовательно, в свойства), но затем, после окончания соответствующего блока, переменные сбрасываются. Но, например, новая переменная, введенная в catch, будет иметь область действия включающей функции / метода.
Gerard ONeill 25.10.2013 19:00:40
Это именно то, что означает блок-бэндинг :)
Benjamin Gruenbaum 25.10.2013 19:01:06

Глобальная сфера:

Глобальные переменные точно такие же, как глобальные звезды (Джеки Чан, Нельсон Мандела). Вы можете получить к ним доступ (получить или установить значение) из любой части вашего приложения. Глобальные функции похожи на глобальные события (Новый год, Рождество). Вы можете выполнить (вызвать) их из любой части вашего приложения.

//global variable
var a = 2;

//global function
function b(){
   console.log(a);  //access global variable
}

Локальный охват:

Если вы находитесь в США, вы можете знать Ким Кардашьян, печально известную знаменитость (ей каким-то образом удается создавать таблоиды). Но люди за пределами США не узнают ее. Она местная звезда, привязанная к своей территории.

Локальные переменные похожи на локальные звезды. Вы можете получить к ним доступ только (получить или установить значение) внутри области. Локальная функция похожа на локальные события - вы можете выполнять только (праздновать) внутри этой области. Если вы хотите получить к ним доступ за пределами области действия, вы получите ошибку ссылки

function b(){
   var d = 21; //local variable
   console.log(d);

   function dog(){  console.log(a); }
     dog(); //execute local function
}

 console.log(d); //ReferenceError: dddddd is not defined    

Проверьте эту статью для глубокого понимания области

8
23.02.2016 18:56:04

JavaScript имеет только два типа области видимости:

  1. Global Scope : Global - это не что иное, как область действия на уровне окна. Здесь переменная присутствует во всем приложении.
  2. Функциональная область : переменная, объявленная внутри функции с varключевым словом, имеет функциональную область.

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

        a = "global";
         function outer(){ 
              b = "local";
              console.log(a+b); //"globallocal"
         }
outer();

Цепочка прицела ->

  1. Уровень окна - aи outerфункция находятся на верхнем уровне в цепочке областей действия.
  2. когда внешняя функция вызвала новую variable scope object(и включена в цепочку областей видимости), добавленную с переменной bвнутри нее.

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

9
21.09.2014 20:49:17
Не уверен, почему это не принятый ответ. На самом деле это просто функциональная область (до ECMA6 не было «локальной области») и глобальные привязки
texasbruce 28.02.2015 21:50:07

запустить код. надеюсь, что это даст представление о области видимости

Name = 'global data';
document.Name = 'current document data';
(function(window,document){
var Name = 'local data';
var myObj = {
    Name: 'object data',
    f: function(){
        alert(this.Name);
    }
};

myObj.newFun = function(){
    alert(this.Name);
}

function testFun(){
    alert("Window Scope : " + window.Name + 
          "\nLocal Scope : " + Name + 
          "\nObject Scope : " + this.Name + 
          "\nCurrent document Scope : " + document.Name
         );
}


testFun.call(myObj);
})(window,document);
8
18.10.2014 09:54:56

В JS есть только функциональные области. Не блокируйте прицелы! Вы можете увидеть, что поднимает тоже.

var global_variable = "global_variable";
var hoisting_variable = "global_hoist";

// Global variables printed
console.log("global_scope: - global_variable: " + global_variable);
console.log("global_scope: - hoisting_variable: " + hoisting_variable);

if (true) {
    // The variable block will be global, on true condition.
    var block = "block";
}
console.log("global_scope: - block: " + block);

function local_function() {
    var local_variable = "local_variable";
    console.log("local_scope: - local_variable: " + local_variable);
    console.log("local_scope: - global_variable: " + global_variable);
    console.log("local_scope: - block: " + block);
    // The hoisting_variable is undefined at the moment.
    console.log("local_scope: - hoisting_variable: " + hoisting_variable);

    var hoisting_variable = "local_hoist";
    // The hoisting_variable is now set as a local one.
    console.log("local_scope: - hoisting_variable: " + hoisting_variable);
}

local_function();

// No variable in a separate function is visible into the global scope.
console.log("global_scope: - local_variable: " + local_variable);
3
11.07.2015 18:13:01
(долгое время с момента публикации ответа) Блокировать область; developer.mozilla.org/en/docs/Web/JavaScript/Reference/…
Bob 22.03.2017 16:13:55

Идея области видимости в JavaScript, изначально разработанная Бренданом Айхом, возникла из языка сценариев HyperCard HyperTalk .

На этом языке показы были сделаны аналогично стопке карточек. Там была мастер-карта, называемая фоном. Это было прозрачно и может быть замечено как нижняя карта. Любой контент на этой базовой карте был предоставлен картам, размещенным поверх нее. Каждая карта, размещенная сверху, имела свой собственный контент, который имел приоритет над предыдущей картой, но при желании имел доступ к предыдущим картам.

Именно так спроектирована система определения объема JavaScript. У него просто разные имена. Карты в JavaScript известны как контексты исполнения ECMA . Каждый из этих контекстов состоит из трех основных частей. Переменная среда, лексическая среда и привязка this. Возвращаясь к справочнику карточек, лексическая среда содержит весь контент из предыдущих карточек ниже в стопке. Текущий контекст находится на вершине стека, и любой объявленный там контент будет храниться в переменной среде. Переменная среда будет иметь приоритет в случае именования коллизий.

Привязка this будет указывать на содержащий объект. Иногда контексты или контексты выполнения изменяются без изменения содержащего объекта, например, в объявленной функции, где содержащийся объект может быть windowили функцией конструктора.

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

Это техническое объяснение. На практике важно помнить, что в JavaScript

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

Применяя это к одному из предыдущих примеров (5. «Закрытие») на этой странице, можно следовать стекам контекстов выполнения. В этом примере в стеке три контекста. Они определяются внешним контекстом, контекстом в немедленно вызываемой функции, вызываемой var шестым, и контекстом в возвращаемой функции внутри немедленно вызываемой функции var шестого.

я ) Внешний контекст. Он имеет переменную среду a = 1
ii ) Контекст IIFE, он имеет лексическую среду a = 1, но переменную среду a = 6, которая имеет приоритет в стеке
iii ) Возвращенный контекст функции, он имеет лексическую окружение a = 6, и это значение, указанное в предупреждении при вызове.

введите описание изображения здесь

25
14.09.2015 20:29:53

В JavaScript есть два типа областей действия.

  1. Глобальная область : переменная, объявленная в глобальной области, может использоваться в любом месте программы очень плавно. Например:

    var carName = " BMW";
    
    // code here can use carName
    
    function myFunction() {
         // code here can use carName 
    }
  2. Функциональная область или локальная область : переменная, объявленная в этой области, может использоваться только в своей собственной функции. Например:

    // code here can not use carName
    function myFunction() {
       var carName = "BMW";
       // code here can use carName
    }
-3
11.01.2016 05:32:02

ПОЧТИ существуют только два типа областей JavaScript:

  • область действия каждого объявления var связана с наиболее непосредственно включающей функцией
  • если нет функции включения для объявления var, это глобальная область

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

var i = 10, v = 10;
for (var i = 0; i < 5; i++) { var v = 5; }
console.log(i, v);
// output 5 5

Использование функций вместо:

var i = 10, v = 10;
$.each([0, 1, 2, 3, 4], function(i) { var v = 5; });
console.log(i,v);
// output 10 10

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

Это почти все, что вам нужно знать с точки зрения определения JavaScript, за исключением:

  • try / catch вводит новую область видимости ТОЛЬКО для самой переменной исключения, другие переменные не имеют новой области видимости
  • with-clause, очевидно, является еще одним исключением, но использование with-clause крайне нежелательно ( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with )

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

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

Итак, этот код:

var i = 1;
function abc() {
  i = 2;
  var i = 3;
}
console.log(i);     // outputs 1

эквивалентно:

var i = 1;
function abc() {
  var i;     // var declaration moved to the top of the scope
  i = 2;
  i = 3;     // the assignment stays where it is
}
console.log(i);

Это может показаться нелогичным, но это имеет смысл с точки зрения императора языкового конструктора.

6
29.10.2015 16:12:19

Старая школа JavaScript

Традиционно JavaScript имеет только два типа области видимости:

  1. Глобальная область : переменные известны во всем приложении с самого начала приложения (*)
  2. Функциональная сфера : переменные известны в той функции, в которой они объявлены, с начала функции (*)

Я не буду подробно останавливаться на этом, поскольку уже есть много других ответов, объясняющих разницу.


Современный JavaScript

В последние спецификации JavaScript теперь также позволяют третий объем:

  1. Блок-область . Идентификаторы «известны» из верхней части области, в которой они объявлены , но они не могут быть назначены или разыменованы (прочитаны) до окончания строки их объявления. Этот промежуточный период называется «временной мертвой зоной».

Как создать переменные области видимости блока?

Традиционно вы создаете свои переменные следующим образом:

var myVariable = "Some text";

Переменные области видимости блока создаются следующим образом:

let myVariable = "Some text";

Так в чем же разница между функциональной областью и областью блока?

Чтобы понять разницу между функциональной областью и областью блока, рассмотрите следующий код:

// i IS NOT known here
// j IS NOT known here
// k IS known here, but undefined
// l IS NOT known here

function loop(arr) {
    // i IS known here, but undefined
    // j IS NOT known here
    // k IS known here, but has a value only the second time loop is called
    // l IS NOT known here

    for( var i = 0; i < arr.length; i++ ) {
        // i IS known here, and has a value
        // j IS NOT known here
        // k IS known here, but has a value only the second time loop is called
        // l IS NOT known here
    };

    // i IS known here, and has a value
    // j IS NOT known here
    // k IS known here, but has a value only the second time loop is called
    // l IS NOT known here

    for( let j = 0; j < arr.length; j++ ) {
        // i IS known here, and has a value
        // j IS known here, and has a value
        // k IS known here, but has a value only the second time loop is called
        // l IS NOT known here
    };

    // i IS known here, and has a value
    // j IS NOT known here
    // k IS known here, but has a value only the second time loop is called
    // l IS NOT known here
}

loop([1,2,3,4]);

for( var k = 0; k < arr.length; k++ ) {
    // i IS NOT known here
    // j IS NOT known here
    // k IS known here, and has a value
    // l IS NOT known here
};

for( let l = 0; l < arr.length; l++ ) {
    // i IS NOT known here
    // j IS NOT known here
    // k IS known here, and has a value
    // l IS known here, and has a value
};

loop([1,2,3,4]);

// i IS NOT known here
// j IS NOT known here
// k IS known here, and has a value
// l IS NOT known here

Здесь мы видим, что наша переменная jизвестна только в первом цикле for, но не до и после. Тем не менее, наша переменная iизвестна во всей функции.

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


Безопасно ли сегодня использовать блочные переменные области видимости?

Насколько безопасно использовать сегодня, зависит от вашей среды:

  • Если вы пишете код JavaScript на стороне сервера ( Node.js ), вы можете смело использовать это letутверждение.

  • Если вы пишете код JavaScript на стороне клиента и используете браузер-транспортер (например, Traceur или babel-standalone ), вы можете смело использовать это letутверждение, однако ваш код, вероятно, будет не оптимальным с точки зрения производительности.

  • Если вы пишете код JavaScript на стороне клиента и используете транспортер на основе Node (например, скрипт оболочки traceur или Babel ), вы можете смело использовать это letутверждение. А поскольку ваш браузер будет знать только о переданном коде, недостатки производительности должны быть ограничены.

  • Если вы пишете код JavaScript на стороне клиента и не используете транспортер, вам следует подумать о поддержке браузера.

    Вот некоторые браузеры, которые вообще не поддерживают let:

    • Internet Explorer 10 и ниже
    • Firefox 43 и ниже
    • Safari 9 и ниже
    • Android-браузер 4 и ниже
    • Опера 27 и ниже
    • Chome 40 и ниже
    • ЛЮБАЯ версия Opera Mini и Blackberry Browser

введите описание изображения здесь


Как отслеживать поддержку браузера

Актуальный обзор того, какие браузеры поддерживают letоператор на момент прочтения этого ответа, см. На этой Can I Useстранице .


(*) Глобальные и функциональные переменные могут быть инициализированы и использованы до того, как объявлены, потому что переменные JavaScript подняты . Это означает, что объявления всегда находятся на вершине области видимости.

75
27.02.2020 17:50:33
«Не известно» вводит в заблуждение, потому что переменная объявлена ​​там из-за подъема.
Oriol 30.08.2016 04:48:19
Приведенный выше пример вводит в заблуждение, переменные «i» и «j» не известны за пределами блока. Переменные «let» имеют область видимости только в этом конкретном блоке, а не за его пределами. Пусть let имеет и другие преимущества: вы не можете повторно объявить переменную снова, и она содержит лексическую область видимости.
zakir 24.01.2018 16:35:25
Это было полезно, спасибо! Я думаю, что было бы еще более полезно определиться с тем, что вы подразумеваете под «современным JavaScript» и «старой школой JavaScript»; Я думаю, что они соответствуют ECMAScript 6 / ES6 / ECMAScript 2015 и более ранним версиям соответственно?
Jon Schneider 6.03.2018 20:57:23
@JonSchneider: Правильно! Там, где я говорю «старый школьный JavaScript», я, в сущности, говорю о ECMAScript 5, а где я ссылаюсь на «современный JavaScript», я имею в виду ECMAScript 6 (он же ECMAScript 2015). Я не думал, что здесь действительно так важно вдаваться в детали, поскольку большинство людей просто хотят знать (1) в чем разница между областью блока и функциональной областью, (2) какие браузеры поддерживают область блока и (3) безопасно ли сегодня использовать область видимости для любого проекта, над которым они работают. Поэтому я сосредоточил свой ответ на решении этих вопросов.
John Slegers 6.03.2018 22:25:51
@JonSchneider: (продолжение) Тем не менее, я только что добавил ссылку на статью Smashing Magazine на ES6 / ES2015 для тех, кто хочет узнать больше о том, какие функции были добавлены в JavaScript за последние пару лет ... кого-либо еще, кто может быть, интересно, что я имею в виду под «современным JavaScript».
John Slegers 6.03.2018 22:27:59

Просто чтобы добавить к другим ответам, область действия - это список поиска всех объявленных идентификаторов (переменных) и обеспечивает строгий набор правил, касающихся того, как они доступны для выполняемого в настоящее время кода. Этот поиск может быть использован для назначения переменной, которая является ссылкой LHS (слева), или для извлечения ее значения, которая является ссылкой RHS (правой стороны). Эти поиски - то, что движок JavaScript делает внутренне, когда он компилирует и выполняет код.

Таким образом, с этой точки зрения, я думаю, что поможет картина, которую я нашел в электронной книге «Области применения и замыкания» Кайла Симпсона:

образ

Цитирую из его книги:

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

Стоит упомянуть одну вещь: «Поиск области останавливается, когда он находит первое совпадение».

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

9
30.03.2016 13:33:04

Насколько я понимаю, есть 3 области: глобальная область, доступная глобально; локальная область, доступная для всей функции независимо от блоков; и область видимости блока, доступная только блоку, выражению или выражению, в котором он был использован. Глобальная и локальная область видимости указываются с помощью ключевого слова var как внутри функции, так и снаружи, а область видимости блока указывается с помощью ключевого слова let.

Для тех, кто считает, что существует только глобальная и локальная область видимости, пожалуйста, объясните, почему у Mozilla есть целая страница, описывающая нюансы области видимости блока в JS.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let

2
16.09.2017 22:35:07

Современные Js, ES6 +, ' const' и ' let'

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

constследует использовать в 95% случаев . Это делает так, чтобы ссылка на переменную не могла измениться. Свойства массива, объекта и узла DOM могут изменяться и, скорее всего, должны быть const.

letследует использовать для любой переменной, ожидающей переназначения. Это включает в себя цикл for. Если вы когда-либо измените значение после инициализации, используйте let.

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

5
8.12.2017 19:03:40

В JavaScript есть два типа области видимости:

  • Локальная сфера
  • Глобальная сфера

Функция Below имеет локальную переменную области видимости carName. И эта переменная не доступна извне функции.

function myFunction() {
    var carName = "Volvo";
    alert(carName);
    // code here can use carName
}

Класс Below имеет переменную глобальной области видимости carName. И эта переменная доступна из любого места в классе.

class {

    var carName = " Volvo";

    // code here can use carName

    function myFunction() {
        alert(carName);
        // code here can use carName 
    }
}
1
1.11.2017 09:16:18

В EcmaScript5 есть в основном две области: локальная область и глобальная область, но в EcmaScript6 у нас есть в основном три области: локальная область, глобальная область и новая область, называемая областью блока .

Пример объема блока:

for ( let i = 0; i < 10; i++)
{
 statement1...
statement2...// inside this scope we can access the value of i, if we want to access the value of i outside for loop it will give undefined.
}
0
15.12.2017 07:49:36

В ECMAScript 6 введены ключевые слова let и const. Эти ключевые слова могут использоваться вместо ключевого слова var. В отличие от ключевого слова var ключевые слова let и const поддерживают объявление локальной области видимости внутри операторов блока.

var x = 10
let y = 10
const z = 10
{
  x = 20
  let y = 20
  const z = 20
  {
    x = 30
    // x is in the global scope because of the 'var' keyword
    let y = 30
    // y is in the local scope because of the 'let' keyword
    const z = 30
    // z is in the local scope because of the 'const' keyword
    console.log(x) // 30
    console.log(y) // 30
    console.log(z) // 30
  }
  console.log(x) // 30
  console.log(y) // 20
  console.log(z) // 20
}

console.log(x) // 30
console.log(y) // 10
console.log(z) // 10
0
9.02.2018 15:37:12

ES5 и раньше:

Переменные в Javascript изначально (до ES6) лексически ограничивались функциональностью. Термин лексически ограниченный означает, что вы можете увидеть область действия переменных, «посмотрев» на код.

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

  1. Когда функция ищет разрешение переменной, она сначала смотрит на свою область видимости. Это тело функции, т. Е. Все, что находится в фигурных скобках {} (за исключением переменных внутри других функций, находящихся в этой области видимости).
  2. Если он не может найти переменную внутри тела функции, он поднимется до цепочки и рассмотрит область действия переменной в функции, в которой была определена функция . Это то, что подразумевается под лексической областью действия, мы можем видеть в коде, где была определена эта функция, и, таким образом, можем определить цепочку области действия, просто взглянув на код.

Пример:

// global scope
var foo = 'global';
var bar = 'global';
var foobar = 'global';

function outerFunc () {
 // outerFunc scope
 var foo = 'outerFunc';
 var foobar = 'outerFunc';
 innerFunc();
 
 function innerFunc(){
 // innerFunc scope
  var foo = 'innerFunc';
  console.log(foo);
  console.log(bar);
  console.log(foobar);
  }
}

outerFunc();

Что происходит , когда мы пытаемся войти переменные foo, barи foobarв консоли следующее:

  1. Мы пытаемся войти в консоль foo, foo находится внутри самой функции innerFunc. Поэтому значение foo разрешается в строку innerFunc.
  2. Мы пытаемся войти в панель бар на консоли, бар не может быть найден внутри самой функции innerFunc. Поэтому нам нужно подняться по цепочке прицелов . Сначала мы посмотрим на внешнюю функцию, в которой innerFuncбыла определена функция . Это функция outerFunc. В области видимости outerFuncмы можем найти переменную bar, которая содержит строку «externalFunc».
  3. foobar не может быть найден во innerFunc. , Поэтому нам нужно подняться по цепочке областей видимости в область видимости innerFunc. Это также не может быть найдено здесь, мы поднимаемся на другой уровень к глобальной области видимости (то есть к самой внешней области видимости). Здесь мы находим переменную foobar, которая содержит строку «global». Если он не найдет переменную после перехода по цепочке областей действия, механизм JS выдаст referenceError .

ES6 (ES 2015) и старше:

Те же самые понятия лексического охвата и области охвата все еще применяются в ES6. Однако были введены новые способы объявления переменных. Есть следующее:

  • let: создает переменную области блока
  • const: создает переменную области блока, которая должна быть инициализирована и не может быть переназначена

Самое большое различие между varи let/ constзаключается в том, что varэто область действия функции, тогда как let/ constэто область действия блока. Вот пример, чтобы проиллюстрировать это:

let letVar = 'global';
var varVar = 'global';

function foo () {
  
  if (true) {
    // this variable declared with let is scoped to the if block, block scoped
    let letVar = 5;
    // this variable declared with let is scoped to the function block, function scoped
    var varVar = 10;
  }
  
  console.log(letVar);
  console.log(varVar);
}


foo();

В приведенном выше примере letVar регистрирует глобальное значение, поскольку переменные, объявленные с помощью, letимеют область видимости блока. Они перестают существовать вне соответствующего блока, поэтому к переменной нельзя обратиться за пределы блока if.

1
4.11.2018 09:19:59

Очень распространенная проблема, еще не описанная, с которой часто сталкиваются внешние кодеры, - это область видимости встроенного обработчика событий в HTML - например, с

<button onclick="foo()"></button>

Область действия переменных, на on*которые может ссылаться атрибут, должна быть:

  • глобальный (работающие встроенные обработчики почти всегда ссылаются на глобальные переменные)
  • свойство документа (например, querySelectorкак будет указывать автономная переменная document.querySelector; редко)
  • свойство элемента, к которому прикреплен обработчик (как выше; редко)

В противном случае вы получите ReferenceError при вызове обработчика. Так, например, если встроенный обработчик ссылается на функцию, которая определена внутри window.onload или $(function() {, ссылка потерпит неудачу, потому что встроенный обработчик может ссылаться только на переменные в глобальной области видимости, а функция не является глобальной:

Свойства documentи свойства элемента, к которому прикреплен обработчик, также могут указываться как автономные переменные внутри встроенных обработчиков, поскольку встроенные обработчики вызываются внутри двух withблоков , один для documentэлемента, другой для элемента. Цепочка области видимости переменных внутри этих обработчиков чрезвычайно неинтуитивна , и обработчику рабочего события, вероятно, потребуется глобальная функция (и, вероятно, следует избегать ненужного глобального загрязнения ).

Поскольку цепочка областей видимости внутри встроенных обработчиков очень странная , а встроенные обработчики требуют глобального загрязнения, а встроенные обработчики иногда требуют экранирования строки при передаче аргументов, вероятно, их легче избежать. Вместо этого присоединяйте обработчики событий, используя Javascript (например, с addEventListener), а не с разметкой HTML.


С другой стороны, в отличие от обычных <script>тегов, которые выполняются на верхнем уровне, код внутри модулей ES6 выполняется в своей собственной области видимости. Переменная, определенная в верхней части обычного <script>тега, является глобальной, поэтому вы можете ссылаться на нее в других <script>тегах, например:

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

Верхний уровень модуля ES6 аналогичен внутреннему уровню IIFE на верхнем уровне в норме <script>. Модуль может ссылаться на любые переменные, которые являются глобальными, и ничто не может ссылаться на что-либо внутри модуля, если модуль явно не предназначен для него.

2
23.02.2020 06:21:19