Какова область видимости переменных в javascript? Имеют ли они одинаковую область внутри, в отличие от внешней функции? Или это вообще имеет значение? Кроме того, где хранятся переменные, если они определены глобально?
Какова область видимости переменных в javascript? Имеют ли они одинаковую область внутри, в отличие от внешней функции? Или это вообще имеет значение? Кроме того, где хранятся переменные, если они определены глобально?
Ответы:
JavaScript имеет лексическую (также называемую статической) область видимости и замыкания. Это означает, что вы можете определить область действия идентификатора, посмотрев на исходный код.
Четыре области применения:
Вне особых случаев глобальной и модульной области, переменные объявляются с использованием var
(область действия функции), let
(область действия блока) и const
(область действия блока). Большинство других форм объявления идентификаторов имеют строгую область видимости блока.
Область действия - это область кодовой базы, по которой действителен идентификатор.
Лексическая среда - это отображение между именами идентификаторов и значениями, связанными с ними.
Область действия формируется из связанного вложения лексических сред, причем каждый уровень вложения соответствует лексическому окружению контекста выполнения предка.
Эти связанные лексические среды образуют «цепочку». Разрешение идентификатора - это процесс поиска по этой цепочке соответствующего идентификатора.
Разрешение идентификатора происходит только в одном направлении: наружу. Таким образом, внешние лексические среды не могут «видеть» внутреннюю лексическую среду.
Есть три соответствующие факторы в определении сферы в качестве идентификатора в JavaScript:
Некоторые из способов идентификации идентификаторов:
var
, let
иconst
var
в нестрогом режиме)import
заявленияeval
Некоторые из идентификаторов местоположений могут быть объявлены:
Идентификаторы, объявленные с использованием, var
имеют область действия функции , за исключением случаев, когда они объявляются непосредственно в глобальном контексте, и в этом случае они добавляются в качестве свойств глобального объекта и имеют глобальную область действия. Существуют отдельные правила их использования в eval
функциях.
Идентификаторы объявлены с использованием 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
строках переменные, объявленные с использованием, 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 область видимости реализуется через лексические среды, связанные друг с другом в «цепочку» внешними ссылками. Эта цепочка лексических сред называется цепочкой областей действия, и разрешение идентификатора происходит путем поиска в цепочке соответствующего идентификатора.
Узнайте более .
Javascript использует цепочки областей действия, чтобы установить область действия для данной функции. Обычно существует одна глобальная область, и каждая определенная функция имеет свою собственную вложенную область. Любая функция, определенная в другой функции, имеет локальную область видимости, которая связана с внешней функцией. Это всегда позиция в источнике, который определяет область действия.
Элемент в цепочке областей действия - это в основном Map с указателем на родительскую область видимости.
При разрешении переменной javascript начинается с самой внутренней области и выполняет поиск за ее пределами.
Переменные, объявленные глобально, имеют глобальную область видимости. Переменные, объявленные внутри функции, относятся к этой функции и скрывают глобальные переменные с тем же именем.
(Я уверен, что есть много тонкостей, на которые настоящие программисты JavaScript смогут указать в других ответах. В частности, я наткнулся на эту страницу о том, что именно this
означает в любое время. Надеюсь, этой более вводной ссылки достаточно, чтобы вы начали, хотя .)
Традиционно JavaScript имеет только два типа области видимости:
Я не буду подробно останавливаться на этом, поскольку уже есть много других ответов, объясняющих разницу.
В последние спецификации JavaScript теперь также позволяют третий объем:
Традиционно вы создаете свои переменные следующим образом:
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
:
Обновленный обзор того, какие браузеры поддерживают let
оператор на момент прочтения этого ответа, см. На этой Can I Use
странице .
(*) Глобальные и функциональные переменные могут быть инициализированы и использованы до того, как объявлены, потому что переменные JavaScript подняты . Это означает, что объявления всегда находятся на вершине области видимости.
Вот пример:
<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>
Вы захотите исследовать замыкания и узнать, как использовать их для создания частных пользователей .
Ключ, насколько я понимаю, состоит в том, что Javascript имеет определение уровня функции по сравнению с более распространенной областью видимости блока C.
В «Javascript 1.7» (расширение Mozilla до Javascript) также можно объявить переменные области блока с помощью let
оператора :
var a = 4;
let (a = 3) {
alert(a); // 3
}
alert(a); // 4
let
.
Идея области видимости в JavaScript, изначально разработанная Бренданом Айхом, возникла из языка сценариев HyperCard HyperTalk .
На этом языке показы были сделаны аналогично стопке карточек. Там была мастер-карта, называемая фоном. Это было прозрачно и может быть замечено как нижняя карта. Любой контент на этой базовой карте был предоставлен картам, размещенным поверх него. Каждая карта, размещенная сверху, имела свой собственный контент, который имел приоритет над предыдущей картой, но при желании имел доступ к предыдущим картам.
Именно так и разработана система определения объема JavaScript. У него просто разные имена. Карты в JavaScript известны как контексты исполнения ECMA . Каждый из этих контекстов состоит из трех основных частей. Переменная среда, лексическая среда и привязка this. Возвращаясь к справочнику карточек, лексическая среда содержит все содержимое предыдущих карточек, находящихся ниже в стопке. Текущий контекст находится на вершине стека, и любой объявленный там контент будет храниться в переменной среде. Переменная среда будет иметь приоритет в случае именования коллизий.
Привязка this будет указывать на содержащий объект. Иногда контексты или контексты выполнения изменяются без изменения содержащего объекта, например, в объявленной функции, где содержащийся объект может быть window
или функцией конструктора.
Эти контексты выполнения создаются каждый раз, когда передается управление. Управление передается, когда код начинает выполняться, и это в основном делается из выполнения функции.
Это техническое объяснение. На практике важно помнить, что в JavaScript
Применяя это к одному из предыдущих примеров (5. «Закрытие») на этой странице, можно следовать стекам контекстов выполнения. В этом примере в стеке три контекста. Они определяются внешним контекстом, контекстом в немедленно вызываемой функции, вызываемой var шестой, и контекстом в возвращаемой функции внутри немедленно вызываемой функции var шестого.
я ) Внешний контекст. Он имеет переменную среду a = 1
ii ) Контекст IIFE, он имеет лексическую среду a = 1, но переменную среду a = 6, которая имеет приоритет в стеке
iii ) Возвращенный контекст функции, он имеет лексическую окружение a = 6, и это значение, указанное в предупреждении при вызове.
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 относительно имен, импортируемых из объекта! Небольшое упоминание имени, которое уже определено, сделает это намного безопаснее. Я лично никогда не буду использовать с из-за этого.
with
оператор является формой видимости блока, но catch
предложения - гораздо более распространенная форма (забавный факт, v8 реализует catch
с a with
) - это практически единственные формы видимости блока в самом JavaScript (то есть функция, глобальная, try / catch , с и их производными), однако хост-среды имеют разные понятия области видимости - например, встроенные события в браузере и модуль VM NodeJS.
Я обнаружил, что многие новички в JavaScript испытывают трудности с пониманием того, что наследование доступно по умолчанию в языке, и что область функций пока является единственной областью действия. Я предоставил расширение для украшения, которое я написал в конце прошлого года, под названием JSPretty. Функциональные цвета функционируют в коде области видимости и всегда связывают цвет со всеми переменными, объявленными в этой области. Закрытие визуально демонстрируется, когда переменная с цветом из одной области видимости используется в другой области видимости.
Попробуйте эту функцию на:
Смотрите демо на:
Посмотреть код на:
В настоящее время эта функция поддерживает 16 вложенных функций, но в настоящее время не окрашивает глобальные переменные.
JavaScript имеет только два типа области видимости:
var
ключевым словом, имеет функциональную область.Всякий раз, когда вызывается функция, создается объект переменной области (и включается в цепочку областей действия), за которой следуют переменные в JavaScript.
a = "global";
function outer(){
b = "local";
console.log(a+b); //"globallocal"
}
outer();
Цепочка прицела ->
a
и outer
функция находятся на верхнем уровне в цепочке областей действия.variable scope object
(и включена в цепочку областей видимости), добавленную с переменной b
внутри нее.Теперь, когда переменная a
требуется, она сначала ищет ближайшую область видимости переменной, а если переменная отсутствует, то она перемещается к следующему объекту цепочки области видимости переменной. В данном случае это уровень окна.
Просто чтобы добавить к другим ответам, область действия - это список поиска всех объявленных идентификаторов (переменных) и обеспечивает строгий набор правил, касающихся того, как они доступны для выполняемого в настоящее время кода. Этот поиск может быть использован для назначения переменной, которая является ссылкой LHS (слева), или для получения ее значения, которая является ссылкой RHS (правой стороны). Эти поиски - то, что движок JavaScript делает внутренне, когда он компилирует и выполняет код.
Таким образом, с этой точки зрения, я думаю, что поможет картина, которую я нашел в электронной книге «Области применения и замыкания» Кайла Симпсона:
Цитирую из его книги:
Здание представляет собой набор правил для вложенной области действия нашей программы. Первый этаж здания представляет вашу текущую область действия, где бы вы ни находились. Верхний уровень здания - глобальная сфера. Вы решаете рекомендации LHS и RHS, просматривая свой текущий этаж, и, если вы его не находите, поднимаетесь на лифте на следующий этаж, смотрите там, затем на следующий и так далее. Как только вы попадаете на верхний этаж (глобальный охват), вы либо находите то, что ищете, либо нет. Но ты должен остановиться независимо.
Стоит упомянуть одну вещь: «Поиск в области видимости прекращается, когда он находит первое совпадение».
Эта идея «уровней области действия» объясняет, почему «это» можно изменить с помощью вновь созданной области действия, если она ищется во вложенной функции. Вот ссылка, которая входит во все эти детали, Все , что вы хотели знать о сфере действия JavaScript
запустить код. надеюсь, что это даст представление о области видимости
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);
Глобальные переменные точно такие же, как глобальные звезды (Джеки Чан, Нельсон Мандела). Вы можете получить к ним доступ (получить или установить значение) из любой части вашего приложения. Глобальные функции похожи на глобальные события (Новый год, Рождество). Вы можете выполнить (вызвать) их из любой части вашего приложения.
//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
ПОЧТИ есть только два типа областей JavaScript:
Таким образом, любые блоки, кроме функций, не создают новую область видимости. Это объясняет, почему циклы 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, за исключением:
Таким образом, вы можете видеть, что область видимости JavaScript на самом деле очень проста, хотя и не всегда интуитивно понятна. Несколько вещей, о которых нужно знать:
Итак, этот код:
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);
Это может показаться нелогичным, но это имеет смысл с точки зрения императора языкового конструктора.
const
' и ' let
'Для каждой создаваемой переменной вы должны использовать блочную область видимости, как и большинство других основных языков. var
является устаревшим . Это делает ваш код безопаснее и удобнее в обслуживании.
const
следует использовать в 95% случаев . Это делает так, чтобы ссылка на переменную не могла измениться. Свойства массива, объекта и узла DOM могут изменяться и, вероятно, должны быть const
.
let
следует использовать для любой переменной, ожидающей переназначения. Это включает в себя цикл for. Если вы когда-либо измените значение после инициализации, используйте let
.
Область видимости блока означает, что переменная будет доступна только в скобках, в которых она объявлена. Это распространяется на внутренние области, включая анонимные функции, созданные в вашей области.
Попробуйте этот любопытный пример. В приведенном ниже примере, если бы 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());
В 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 области: глобальная область, доступная глобально; локальная область, доступная для всей функции независимо от блоков; и область видимости блока, доступная только блоку, выражению или выражению, в котором он был использован. Глобальная и локальная область видимости указываются с помощью ключевого слова var как внутри функции, так и снаружи, а область видимости блока указывается с помощью ключевого слова let.
Для тех, кто считает, что существует только глобальная и локальная область действия, пожалуйста, объясните, почему у Mozilla будет целая страница, описывающая нюансы области действия блока в JS.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let
Очень распространенная проблема, еще не описанная, с которой часто сталкиваются внешние кодеры, - это область видимости встроенного обработчика событий в 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>
. Модуль может ссылаться на любые переменные, которые являются глобальными, и ничто не может ссылаться на что-либо внутри модуля, если модуль явно не предназначен для него.
В 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
}
}
ES5
и раньше:Переменные в Javascript изначально (до ES6
) лексически ограничивались функциональностью. Термин лексически ограниченный означает, что вы можете увидеть область действия переменных, «посмотрев» на код.
Каждая переменная, объявленная с var
ключевым словом, попадает в область видимости функции. Однако, если в этой функции объявлена другая функция, эти функции будут иметь доступ к переменным внешних функций. Это называется цепочкой контекста . Это работает следующим образом:
// 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
в консоли следующее:
innerFunc
. Поэтому значение foo разрешается в строку innerFunc
.innerFunc
. Поэтому нам нужно подняться по цепочке прицелов . Сначала мы посмотрим на внешнюю функцию, в которой innerFunc
была определена функция . Это функция outerFunc
. В области видимости outerFunc
мы можем найти переменную bar, которая содержит строку «externalFunc».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.
В 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.
}
В 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
Мне действительно нравится принятый ответ, но я хочу добавить это:
Scope собирает и поддерживает список поиска всех объявленных идентификаторов (переменных) и обеспечивает строгий набор правил, касающихся того, насколько они доступны для выполняемого в настоящее время кода.
Область действия - это набор правил для поиска переменных по имени их идентификатора.
В JavaScript есть два типа областей действия.
Глобальная область : переменная, объявленная в глобальной области, может использоваться в любом месте программы очень плавно. Например:
var carName = " BMW";
// code here can use carName
function myFunction() {
// code here can use carName
}
Функциональная область или локальная область : переменная, объявленная в этой области, может использоваться только в своей собственной функции. Например:
// code here can not use carName
function myFunction() {
var carName = "BMW";
// code here can use carName
}