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

2013

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

lYriCAlsSH
источник
4
Вот еще одна приятная ссылка, чтобы помнить об этой проблеме: « Объяснение области действия JavaScript и замыканий ».
Инженер-программист полного стека
9
Вот статья, которая объясняет это очень хорошо. Все, что вам нужно знать о области видимости переменной Javascript
Saurab Parakh
2
Упоминался ранее электронная книга Кайл Симпсона доступна для чтения на Github, и он говорит вам все , что вам нужно знать о JavaScript Скоупсе и колпачках. Вы можете найти его здесь: github.com/getify/You-Dont-Know-JS/blob/master/… Это часть серии книг «Вы не знаете JS» , которая отлично подходит для всех, кто хотел бы знать больше о JavaScript.
3rik82

Ответы:

2536

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 область видимости реализуется через лексические среды, связанные друг с другом в «цепочку» внешними ссылками. Эта цепочка лексических сред называется цепочкой областей действия, и разрешение идентификатора происходит путем поиска в цепочке соответствующего идентификатора.

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

Триптих
источник
280
Даже близко не быть исчерпывающим, но это, возможно, необходимый набор приемов Javascript, которые необходимы, чтобы эффективно ПРОЧИТАТЬ современный Javascript.
Триптих
148
Высоко оцененный ответ, не уверен почему. Это просто набор примеров без надлежащего объяснения, а затем, кажется, путает наследование прототипа (то есть разрешение свойства) с цепочкой области видимости (то есть разрешение переменной). Полное (и точное) объяснение области видимости и разрешения свойств содержится в примечаниях к часто задаваемым вопросам comp.lang.javascript .
RobG
109
@RobG Его высоко ценят, потому что он полезен и понятен широкому кругу программистов, несмотря на незначительный катахрез. Ссылка, которую вы разместили, хотя и полезна для некоторых профессионалов, сегодня непонятна большинству людей, пишущих Javascript. Не стесняйтесь исправлять любые проблемы с номенклатурой, редактируя ответ.
Триптих
7
@ триптих - я только редактирую ответы, чтобы исправить мелкие вещи, а не основные. Изменение «области действия» на «свойство» исправит ошибку, но не проблему смешивания наследования и области без очень четкого различия.
RobG
24
Если вы определяете переменную во внешней области видимости, а затем используете оператор if, определите переменную внутри функции с тем же именем, даже если это, если ветвь не достигнута, она будет переопределена. Пример - jsfiddle.net/3CxVm
Крис С
233

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

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

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

krosenvold
источник
1
Цепочки контекста - еще один термин для замыканий [памяти] ... для тех, кто читает здесь, чтобы узнать / войти в javascript.
Новая Александрия
108

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

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

Джон Скит
источник
7
Я боюсь даже начать отвечать на этот вопрос. Как настоящий программист Javascript, я знаю, как быстро ответ может выйти из-под контроля. Хорошие статьи.
Триптих
10
@Triptych: Я знаю, что ты имеешь в виду, когда дела выходят из-под контроля, но все равно , пожалуйста, добавь ответ. Я получил вышеупомянутое, просто сделав пару поисков ... ответ, написанный кем-то с реальным опытом, обязательно будет лучше. Пожалуйста, исправьте любой мой ответ, который, безусловно, неверен!
Джон Скит
4
Почему-то Джон Скит отвечает за МОЙ самый популярный ответ о переполнении стека.
Триптих
75

Старая школа 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 подняты . Это означает, что объявления всегда находятся на вершине области видимости.

Джон Слегерс
источник
2
«Не известно» вводит в заблуждение, потому что переменная объявлена ​​там из-за подъема.
Ориоль
Приведенный выше пример вводит в заблуждение, переменные «i» и «j» не известны за пределами блока. Переменные «let» имеют область видимости только в этом конкретном блоке, а не за его пределами. Пусть let имеет и другие преимущества, вы не можете повторно объявить переменную снова, и она содержит лексическую область видимости.
Закир
1
Это было полезно, спасибо! Я думаю, что было бы еще более полезно определиться с тем, что вы подразумеваете под «современным JavaScript» и «старой школой JavaScript»; Я думаю, что они соответствуют ECMAScript 6 / ES6 / ECMAScript 2015 и более ранним версиям соответственно?
Джон Шнайдер
1
@JonSchneider: Правильно! Там, где я говорю «JavaScript старой школы», я, в сущности, говорю о ECMAScript 5, а когда я ссылаюсь на «современный JavaScript», я имею в виду ECMAScript 6 (он же ECMAScript 2015). Я не думал, что здесь действительно так важно вдаваться в детали, поскольку большинство людей просто хотят знать (1) в чем разница между областью блока и функциональной областью, (2) какие браузеры поддерживают область блока и (3) безопасно ли сегодня использовать область видимости для любого проекта, над которым они работают. Поэтому я сосредоточил свой ответ на решении этих вопросов.
Джон Слегерс
1
@JonSchneider: (продолжение) Тем не менее, я только что добавил ссылку на статью Smashing Magazine на ES6 / ES2015 для тех, кто хочет узнать больше о том, какие функции были добавлены в JavaScript за последние пару лет ... кого-либо еще, кто может быть, интересно, что я имею в виду под «современным JavaScript».
Джон Слегерс
39

Вот пример:

<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>

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

geowa4
источник
31

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

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

Джеймс МакМэхон
источник
26

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

 var a = 4;
 let (a = 3) {
   alert(a); // 3
 }
 alert(a);   // 4
kennytm
источник
2
Да, но безопасно ли это использовать? Я имею в виду, я бы реально выбрал эту реализацию, если мой код будет работать в WebKit?
Игорь Ганапольский
10
@Python: Нет, WebKit не поддерживает let.
Kennytm
Полагаю, единственное правильное использование этого было бы, если бы вы знали, что все клиенты будут использовать браузер Mozilla, как для внутренней системы компании.
ГазБ
Или, если вы программируете, используя платформу XUL, интерфейсную среду Mozilla, где вы создаете, используя css, xml и javascript.
Джерард ONeill
1
@GazB даже это ужасная идея! Итак, сегодня вы знаете, что ваши клиенты используют Mozilla, а затем выходит новое сообщение о том, что теперь они используют что-то другое. То есть причина, по которой наша система оплаты - отстой ... Вы должны использовать IE8, а не IE9, IE10, Firefox или Chrome, потому что это не сработает ...
buzzsawddog
25

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

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

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

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

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

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

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

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

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

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

Трэвис Дж
источник
17

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 относительно имен, импортируемых из объекта! Небольшое упоминание имени, которое уже определено, сделает это намного безопаснее. Я лично никогда не буду использовать с из-за этого.

Жерар ОНЕЙЛ
источник
У вас есть некоторые ошибки, потому что один JavaScript имеет формы блочной видимости.
Бенджамин Грюнбаум
Мои уши (глаза) открыты, Бенджамин. Мои высказанные выше заявления относятся к тому, как я лечу Javascript, но они не основаны на чтении спецификации. И я надеюсь, что вы не имеете в виду оператор with (который является формой видимости объекта) или специальный синтаксис Mozilla 'let'.
Джерард ONeill
Хорошо, withоператор является формой видимости блока, но catchпредложения - гораздо более распространенная форма (забавный факт, v8 реализует catchс a with) - это практически единственные формы видимости блока в самом JavaScript (то есть функция, глобальная, try / catch , с и их производными), однако хост-среды имеют разные понятия области видимости - например, встроенные события в браузере и модуль VM NodeJS.
Бенджамин Грюнбаум
Бенджамин - из того, что я вижу, как с помощью, так и с помощью catch только вводит объект в текущую область (и, следовательно, в свойства), но затем после завершения соответствующего блока переменные сбрасываются. Но, например, новая переменная, введенная в catch, будет иметь область действия включающей функции / метода.
Джерард ONeill
2
Это именно то, что значит блокобзор :)
Бенджамин Грюнбаум
9

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

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

Смотрите демо на:

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

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

austincheney
источник
1
У меня не работает Firefox 26. Я вставляю код или загружаю файл, нажимаю «Выполнить», и ничего не происходит.
mplwork
Сфера и наследование две разные вещи.
Бен Астон
9

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требуется, она сначала ищет ближайшую область видимости переменной, а если переменная отсутствует, то она перемещается к следующему объекту цепочки области видимости переменной. В данном случае это уровень окна.

Anshul
источник
1
Не уверен, почему это не принятый ответ. На самом деле есть только функциональная область (до ECMA6 не было «локальной области») и глобальные привязки
texasbruce
9

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

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

образ

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

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

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

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

Джеймс Дринкард
источник
8

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

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

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

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

//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    

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

Джанкар Махбуб
источник
6

ПОЧТИ есть только два типа областей 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);

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

jackbean818
источник
5

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

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

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

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

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

Gibolt
источник
3

Попробуйте этот любопытный пример. В приведенном ниже примере, если бы 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());
Mig82
источник
3

В 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);
koredalin
источник
(долгое время с момента публикации ответа) Блокировать область; developer.mozilla.org/en/docs/Web/JavaScript/Reference/…
Боб
2

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

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

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

mrmaclean89
источник
2

Очень распространенная проблема, еще не описанная, с которой часто сталкиваются внешние кодеры, - это область видимости встроенного обработчика событий в 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>. Модуль может ссылаться на любые переменные, которые являются глобальными, и ничто не может ссылаться на что-либо внутри модуля, если модуль явно не предназначен для него.

CertainPerformance
источник
1

В 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

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.

Виллем ван дер Веен
источник
0

В 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

В 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

Мне действительно нравится принятый ответ, но я хочу добавить это:

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

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

  • Если переменная не может быть найдена в непосредственной области действия, Engine обращается к следующей внешней содержащей области, продолжая, пока не будет найдена или пока не будет достигнута самая внешняя (иначе говоря, глобальная) область.
  • Набор правил, который определяет, где и как можно искать переменную (идентификатор). Этот поиск может быть использован для назначения переменной, которая является ссылкой LHS (слева), или для получения ее значения, которая является ссылкой RHS (правой стороны). ,
  • Ссылки LHS являются результатом операций присваивания. Связанные с областью присваивания могут выполняться либо с помощью оператора =, либо путем передачи аргументов (присваивания) параметрам функции.
  • Механизм JavaScript сначала компилирует код перед его выполнением, и при этом он разделяет операторы вроде var a = 2; в два отдельных этапа: 1-й. Во-первых, var a, чтобы объявить это в этой области. Это выполняется в начале, перед выполнением кода. Второй. Позже, a = 2, чтобы найти переменную (ссылка LHS) и присвоить ей, если она найдена.
  • Обе справочные проверки как LHS, так и RHS начинаются с текущей выполняемой области, и, если это необходимо (то есть они не находят там того, что ищут), они продвигаются вверх во вложенную область, одну область (этаж) ), ища идентификатор, пока они не дойдут до глобального (верхний этаж) и не остановятся, и либо найдут его, либо нет. Невыполненные ссылки RHS приводят к выдаче ReferenceError. Невыполненные ссылки LHS приводят к автоматическому, неявно созданному глобалу с таким именем (если не в строгом режиме) или к ReferenceError (если в строгом режиме).
  • Область действия состоит из серии «пузырей», каждый из которых действует как контейнер или блок, в котором объявлены идентификаторы (переменные, функции). Эти пузырьки аккуратно вкладываются друг в друга, и эта вложенность определяется во время создания.
Ахмед ХаШаба
источник
-3

В 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
    }
А. Рандхава
источник