Какова область видимости переменных в javascript? Имеют ли они одинаковую область внутри, в отличие от внешней функции? Или это вообще имеет значение? Кроме того, где хранятся переменные, если они определены глобально?
Упоминался ранее электронная книга Кайл Симпсона доступна для чтения на Github, и он говорит вам все , что вам нужно знать о JavaScript Скоупсе и колпачках. Вы можете найти его здесь: github.com/getify/You-Dont-Know-JS/blob/master/… Это часть серии книг «Вы не знаете JS» , которая отлично подходит для всех, кто хотел бы знать больше о JavaScript.
3rik82
Ответы:
2536
TLDR
JavaScript имеет лексическую (также называемую статической) область видимости и замыкания. Это означает, что вы можете определить область действия идентификатора, посмотрев на исходный код.
Четыре области применения:
Глобальный - виден всем
Функция - видимая внутри функции (и ее подфункций и блоков)
Блок - видимый внутри блока (и его подблоков)
Модуль - видимый внутри модуля
Вне особых случаев глобальной и модульной области, переменные объявляются с использованием var(область действия функции), let(область действия блока) и const(область действия блока). Большинство других форм объявления идентификаторов имеют строгую область видимости блока.
обзор
Область действия - это область кодовой базы, по которой действителен идентификатор.
Лексическая среда - это отображение между именами идентификаторов и значениями, связанными с ними.
Область действия формируется из связанного вложения лексических сред, причем каждый уровень вложения соответствует лексическому окружению контекста выполнения предка.
Эти связанные лексические среды образуют «цепочку». Разрешение идентификатора - это процесс поиска по этой цепочке соответствующего идентификатора.
Разрешение идентификатора происходит только в одном направлении: наружу. Таким образом, внешние лексические среды не могут «видеть» внутреннюю лексическую среду.
Есть три соответствующие факторы в определении сферы в качестве идентификатора в JavaScript:
Некоторые из способов идентификации идентификаторов:
var, letиconst
Параметры функции
Параметр блока захвата
Объявления функций
Выражения именованных функций
Неявно определенные свойства глобального объекта (т. Е. Пропущенные varв нестрогом режиме)
import заявления
eval
Некоторые из идентификаторов местоположений могут быть объявлены:
Глобальный контекст
Тело функции
Обычный блок
Вершина управляющей структуры (например, цикл, если, в то время и т. Д.)
Орган управления корпусом
Модули
Декларация Стили
вар
Идентификаторы, объявленные с использованием, 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 =1let y =1const 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 =1let y =1const 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
... из-за этого поведения вы должны быть осторожны при закрытии переменных, объявленных 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
Переменные , объявленные с использованием var, letили constвсе области видимости модулей:
// module1.jsvar x =0exportfunction f(){}//module2.jsimport f from'module1.js'
console.log(x)// throws ReferenceError
Далее будет объявлено свойство для глобального объекта, потому что переменные, объявленные с использованием varв глобальном контексте, добавляются как свойства к глобальному объекту:
var x =1
console.log(window.hasOwnProperty('x'))// true
Область действия определяется как лексическая область кода, по которой действителен идентификатор.
В JavaScript, каждая функция-объект имеет скрытую [[Environment]]ссылку , которая является ссылкой на лексическую среду в контексте выполнения (кадре стеки) , в течение которого он был создан.
Когда вы вызываете функцию, вызывается скрытый [[Call]]метод. Этот метод создает новый контекст выполнения и устанавливает связь между новым контекстом выполнения и лексической средой объекта-функции. Это делается путем копирования [[Environment]]значения объекта-функции во внешнее поле ссылки в лексической среде нового контекста выполнения.
Обратите внимание, что эта связь между новым контекстом выполнения и лексической средой объекта функции называется замыканием .
Таким образом, в JavaScript область видимости реализуется через лексические среды, связанные друг с другом в «цепочку» внешними ссылками. Эта цепочка лексических сред называется цепочкой областей действия, и разрешение идентификатора происходит путем поиска в цепочке соответствующего идентификатора.
Даже близко не быть исчерпывающим, но это, возможно, необходимый набор приемов Javascript, которые необходимы, чтобы эффективно ПРОЧИТАТЬ современный Javascript.
Триптих
148
Высоко оцененный ответ, не уверен почему. Это просто набор примеров без надлежащего объяснения, а затем, кажется, путает наследование прототипа (то есть разрешение свойства) с цепочкой области видимости (то есть разрешение переменной). Полное (и точное) объяснение области видимости и разрешения свойств содержится в примечаниях к часто задаваемым вопросам comp.lang.javascript .
RobG
109
@RobG Его высоко ценят, потому что он полезен и понятен широкому кругу программистов, несмотря на незначительный катахрез. Ссылка, которую вы разместили, хотя и полезна для некоторых профессионалов, сегодня непонятна большинству людей, пишущих Javascript. Не стесняйтесь исправлять любые проблемы с номенклатурой, редактируя ответ.
Триптих
7
@ триптих - я только редактирую ответы, чтобы исправить мелкие вещи, а не основные. Изменение «области действия» на «свойство» исправит ошибку, но не проблему смешивания наследования и области без очень четкого различия.
RobG
24
Если вы определяете переменную во внешней области видимости, а затем используете оператор if, определите переменную внутри функции с тем же именем, даже если это, если ветвь не достигнута, она будет переопределена. Пример - jsfiddle.net/3CxVm
Крис С
233
Javascript использует цепочки областей действия, чтобы установить область действия для данной функции. Обычно существует одна глобальная область, и каждая определенная функция имеет свою собственную вложенную область. Любая функция, определенная в другой функции, имеет локальную область видимости, которая связана с внешней функцией. Это всегда позиция в источнике, который определяет область действия.
Элемент в цепочке областей действия - это в основном Map с указателем на родительскую область видимости.
При разрешении переменной javascript начинается с самой внутренней области и выполняет поиск за ее пределами.
Цепочки контекста - еще один термин для замыканий [памяти] ... для тех, кто читает здесь, чтобы узнать / войти в javascript.
Новая Александрия
108
Переменные, объявленные глобально, имеют глобальную область видимости. Переменные, объявленные внутри функции, относятся к этой функции и скрывают глобальные переменные с тем же именем.
(Я уверен, что есть много тонкостей, на которые настоящие программисты JavaScript смогут указать в других ответах. В частности, я наткнулся на эту страницу о том, что именно thisозначает в любое время. Надеюсь, этой более вводной ссылки достаточно, чтобы вы начали, хотя .)
Я боюсь даже начать отвечать на этот вопрос. Как настоящий программист Javascript, я знаю, как быстро ответ может выйти из-под контроля. Хорошие статьи.
Триптих
10
@Triptych: Я знаю, что ты имеешь в виду, когда дела выходят из-под контроля, но все равно , пожалуйста, добавь ответ. Я получил вышеупомянутое, просто сделав пару поисков ... ответ, написанный кем-то с реальным опытом, обязательно будет лучше. Пожалуйста, исправьте любой мой ответ, который, безусловно, неверен!
Джон Скит
4
Почему-то Джон Скит отвечает за МОЙ самый популярный ответ о переполнении стека.
Триптих
75
Старая школа 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 herefunction 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 herefor(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 herefor(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 подняты . Это означает, что объявления всегда находятся на вершине области видимости.
«Не известно» вводит в заблуждение, потому что переменная объявлена там из-за подъема.
Ориоль
Приведенный выше пример вводит в заблуждение, переменные «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.globalVariablefunction aGlobal( param ){//==window.aGlobal(); //param is only accessible in this functionvar 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.
Да, но безопасно ли это использовать? Я имею в виду, я бы реально выбрал эту реализацию, если мой код будет работать в WebKit?
Игорь Ганапольский
10
@Python: Нет, WebKit не поддерживает let.
Kennytm
Полагаю, единственное правильное использование этого было бы, если бы вы знали, что все клиенты будут использовать браузер Mozilla, как для внутренней системы компании.
ГазБ
Или, если вы программируете, используя платформу XUL, интерфейсную среду Mozilla, где вы создаете, используя css, xml и javascript.
Джерард ONeill
1
@GazB даже это ужасная идея! Итак, сегодня вы знаете, что ваши клиенты используют Mozilla, а затем выходит новое сообщение о том, что теперь они используют что-то другое. То есть причина, по которой наша система оплаты - отстой ... Вы должны использовать IE8, а не IE9, IE10, Firefox или Chrome, потому что это не сработает ...
На этом языке показы были сделаны аналогично стопке карточек. Там была мастер-карта, называемая фоном. Это было прозрачно и может быть замечено как нижняя карта. Любой контент на этой базовой карте был предоставлен картам, размещенным поверх него. Каждая карта, размещенная сверху, имела свой собственный контент, который имел приоритет над предыдущей картой, но при желании имел доступ к предыдущим картам.
Именно так и разработана система определения объема 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 definedwith(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. Функциональные цвета функционируют в коде области видимости и всегда связывают цвет со всеми переменными, объявленными в этой области. Закрытие визуально демонстрируется, когда переменная с цветом из одной области видимости используется в другой области видимости.
У меня не работает Firefox 26. Я вставляю код или загружаю файл, нажимаю «Выполнить», и ничего не происходит.
mplwork
Сфера и наследование две разные вещи.
Бен Астон
9
JavaScript имеет только два типа области видимости:
Global Scope : Global - это не что иное, как область действия на уровне окна. Здесь переменная присутствует во всем приложении.
Функциональная область : переменная, объявленная в функции с varключевым словом, имеет функциональную область.
Всякий раз, когда вызывается функция, создается объект переменной области (и включается в цепочку областей действия), за которой следуют переменные в JavaScript.
a ="global";function outer(){
b ="local";
console.log(a+b);//"globallocal"}
outer();
Цепочка прицела ->
Уровень окна - aи outerфункция находятся на верхнем уровне в цепочке областей действия.
когда внешняя функция вызвала новую variable scope object(и включена в цепочку областей видимости), добавленную с переменной bвнутри нее.
Теперь, когда переменная aтребуется, она сначала ищет ближайшую область видимости переменной, а если переменная отсутствует, то она перемещается к следующему объекту цепочки области видимости переменной. В данном случае это уровень окна.
Не уверен, почему это не принятый ответ. На самом деле есть только функциональная область (до ECMA6 не было «локальной области») и глобальные привязки
texasbruce
9
Просто чтобы добавить к другим ответам, область действия - это список поиска всех объявленных идентификаторов (переменных) и обеспечивает строгий набор правил, касающихся того, как они доступны для выполняемого в настоящее время кода. Этот поиск может быть использован для назначения переменной, которая является ссылкой LHS (слева), или для получения ее значения, которая является ссылкой RHS (правой стороны). Эти поиски - то, что движок JavaScript делает внутренне, когда он компилирует и выполняет код.
Таким образом, с этой точки зрения, я думаю, что поможет картина, которую я нашел в электронной книге «Области применения и замыкания» Кайла Симпсона:
Цитирую из его книги:
Здание представляет собой набор правил для вложенной области действия нашей программы. Первый этаж здания представляет вашу текущую область действия, где бы вы ни находились. Верхний уровень здания - глобальная сфера. Вы решаете рекомендации LHS и RHS, просматривая свой текущий этаж, и, если вы его не находите, поднимаетесь на лифте на следующий этаж, смотрите там, затем на следующий и так далее. Как только вы попадаете на верхний этаж (глобальный охват), вы либо находите то, что ищете, либо нет. Но ты должен остановиться независимо.
Стоит упомянуть одну вещь: «Поиск в области видимости прекращается, когда он находит первое совпадение».
Эта идея «уровней области действия» объясняет, почему «это» можно изменить с помощью вновь созданной области действия, если она ищется во вложенной функции. Вот ссылка, которая входит во все эти детали, Все , что вы хотели знать о сфере действия JavaScript
Глобальные переменные точно такие же, как глобальные звезды (Джеки Чан, Нельсон Мандела). Вы можете получить к ним доступ (получить или установить значение) из любой части вашего приложения. Глобальные функции похожи на глобальные события (Новый год, Рождество). Вы можете выполнить (вызвать) их из любой части вашего приложения.
//global variablevar a =2;//global functionfunction 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
область действия каждого объявления 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 вводит новую область видимости ТОЛЬКО для самой переменной исключения, другие переменные не имеют новой области видимости
Таким образом, вы можете видеть, что область видимости 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);
Это может показаться нелогичным, но это имеет смысл с точки зрения императора языкового конструктора.
Для каждой создаваемой переменной вы должны использовать блочную область видимости, как и большинство других основных языков. varявляется устаревшим . Это делает ваш код безопаснее и удобнее в обслуживании.
constследует использовать в 95% случаев . Это делает так, чтобы ссылка на переменную не могла измениться. Свойства массива, объекта и узла DOM могут изменяться и, вероятно, должны быть const.
letследует использовать для любой переменной, ожидающей переназначения. Это включает в себя цикл for. Если вы когда-либо измените значение после инициализации, используйте let.
Область видимости блока означает, что переменная будет доступна только в скобках, в которых она объявлена. Это распространяется на внутренние области, включая анонимные функции, созданные в вашей области.
Попробуйте этот любопытный пример. В приведенном ниже примере, если бы a было числовым значением, инициализированным в 0, вы бы увидели 0, а затем 1. За исключением того, что a является объектом, и javascript передаст f1 указатель, а не его копию. В результате вы получаете одно и то же предупреждение оба раза.
var a =newDate();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.
Очень распространенная проблема, еще не описанная, с которой часто сталкиваются внешние кодеры, - это область видимости встроенного обработчика событий в HTML - например, с
<buttononclick="foo()"></button>
Область действия переменных, на on*которые может ссылаться атрибут, должна быть:
глобальный (работающие встроенные обработчики почти всегда ссылаются на глобальные переменные)
свойство документа (например, querySelectorкак будет указывать автономная переменная document.querySelector; редко)
свойство элемента, к которому прикреплен обработчик (как выше; редко)
В противном случае вы получите ReferenceError при вызове обработчика. Так, например, если встроенный обработчик ссылается на функцию, которая определена внутриwindow.onload или $(function() {, ссылка потерпит неудачу, потому что встроенный обработчик может ссылаться только на переменные в глобальной области видимости, а функция не является глобальной:
Свойства documentи свойства элемента, к которому прикреплен обработчик, также могут указываться как автономные переменные внутри встроенных обработчиков, поскольку встроенные обработчики вызываются внутри двух withблоков , один для documentэлемента, другой для элемента. Цепочка области видимости переменных внутри этих обработчиков чрезвычайно неинтуитивна , и обработчику рабочего события, вероятно, потребуется глобальная функция (и, вероятно, следует избегать ненужного глобального загрязнения ).
Поскольку цепочка областей действия внутри встроенных обработчиков очень странная , а встроенные обработчики требуют глобального загрязнения, а встроенные обработчики иногда требуют экранирования строки при передаче аргументов, вероятно, их легче избежать. Вместо этого присоединяйте обработчики событий, используя Javascript (например, с addEventListener), а не с разметкой HTML.
С другой стороны, в отличие от обычных <script>тегов, которые выполняются на верхнем уровне, код внутри модулей ES6 выполняется в своей собственной области видимости. Переменная, определенная в верхней части обычного <script>тега, является глобальной, поэтому вы можете ссылаться на нее в других <script>тегах, например так:
Но верхний уровень модуля ES6 не является глобальным. Переменная, объявленная в верхней части модуля ES6, будет видна только внутри этого модуля, если только переменная exportне задана явно или если она не присвоена свойству глобального объекта.
<scripttype="module">const foo ='foo';</script><script>// Can't access foo here, because the other script is a module
console.log(typeof foo);</script>
Верхний уровень модуля ES6 аналогичен внутреннему уровню IIFE на верхнем уровне в норме <script>. Модуль может ссылаться на любые переменные, которые являются глобальными, и ничто не может ссылаться на что-либо внутри модуля, если модуль явно не предназначен для него.
Переменные в Javascript изначально (до ES6) лексически ограничивались функциональностью. Термин лексически ограниченный означает, что вы можете увидеть область действия переменных, «посмотрев» на код.
Каждая переменная, объявленная с varключевым словом, попадает в область видимости функции. Однако, если в этой функции объявлена другая функция, эти функции будут иметь доступ к переменным внешних функций. Это называется цепочкой контекста . Это работает следующим образом:
Когда функция ищет разрешение переменной, она сначала смотрит на свою область видимости. Это тело функции, т. Е. Все, что находится в фигурных скобках {} (за исключением переменных внутри других функций, находящихся в этой области видимости).
Если он не может найти переменную внутри тела функции, он поднимется до цепочки и рассмотрит область действия переменной в функции, в которой была определена функция . Это то, что подразумевается под лексической областью действия, мы можем видеть в коде, где была определена эта функция, и, таким образом, можно определить цепочку области действия, просто взглянув на код.
Что происходит , когда мы пытаемся войти переменные foo, barи foobarв консоли следующее:
Мы пытаемся войти в консоль foo, foo находится внутри самой функции innerFunc. Поэтому значение foo разрешается в строку innerFunc.
Мы пытаемся войти в панель бар на консоли, бар не может быть найден внутри самой функции innerFunc. Поэтому нам нужно подняться по цепочке прицелов . Сначала мы посмотрим на внешнюю функцию, в которой innerFuncбыла определена функция . Это функция outerFunc. В области видимости outerFuncмы можем найти переменную bar, которая содержит строку «externalFunc».
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 scopedlet letVar =5;// this variable declared with let is scoped to the function block, function scopedvar 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 =10let y =10const z =10{
x =20let y =20const z =20{
x =30// x is in the global scope because of the 'var' keywordlet y =30// y is in the local scope because of the 'let' keywordconst 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 собирает и поддерживает список поиска всех объявленных идентификаторов (переменных) и обеспечивает строгий набор правил, касающихся того, насколько они доступны для выполняемого в настоящее время кода.
Область действия - это набор правил для поиска переменных по имени их идентификатора.
Если переменная не может быть найдена в непосредственной области действия, Engine обращается к следующей внешней содержащей области, продолжая, пока не будет найдена или пока не будет достигнута самая внешняя (иначе говоря, глобальная) область.
Набор правил, который определяет, где и как можно искать переменную (идентификатор). Этот поиск может быть использован для назначения переменной, которая является ссылкой LHS (слева), или для получения ее значения, которая является ссылкой RHS (правой стороны). ,
Ссылки LHS являются результатом операций присваивания. Связанные с областью присваивания могут выполняться либо с помощью оператора =, либо путем передачи аргументов (присваивания) параметрам функции.
Механизм JavaScript сначала компилирует код перед его выполнением, и при этом он разделяет операторы вроде var a = 2; в два отдельных этапа: 1-й. Во-первых, var a, чтобы объявить это в этой области. Это выполняется в начале, перед выполнением кода. Второй. Позже, a = 2, чтобы найти переменную (ссылка LHS) и присвоить ей, если она найдена.
Обе справочные проверки как LHS, так и RHS начинаются с текущей выполняемой области, и, если это необходимо (то есть они не находят там того, что ищут), они продвигаются вверх во вложенную область, одну область (этаж) ), ища идентификатор, пока они не дойдут до глобального (верхний этаж) и не остановятся, и либо найдут его, либо нет. Невыполненные ссылки RHS приводят к выдаче ReferenceError. Невыполненные ссылки LHS приводят к автоматическому, неявно созданному глобалу с таким именем (если не в строгом режиме) или к ReferenceError (если в строгом режиме).
Область действия состоит из серии «пузырей», каждый из которых действует как контейнер или блок, в котором объявлены идентификаторы (переменные, функции). Эти пузырьки аккуратно вкладываются друг в друга, и эта вложенность определяется во время создания.
Глобальная область : переменная, объявленная в глобальной области, может использоваться в любом месте программы очень плавно. Например:
var carName =" BMW";// code here can use carNamefunction myFunction(){// code here can use carName }
Функциональная область или локальная область : переменная, объявленная в этой области, может использоваться только в своей собственной функции. Например:
// code here can not use carNamefunction myFunction(){var carName ="BMW";// code here can use carName}
Ответы:
TLDR
JavaScript имеет лексическую (также называемую статической) область видимости и замыкания. Это означает, что вы можете определить область действия идентификатора, посмотрев на исходный код.
Четыре области применения:
Вне особых случаев глобальной и модульной области, переменные объявляются с использованием
var
(область действия функции),let
(область действия блока) иconst
(область действия блока). Большинство других форм объявления идентификаторов имеют строгую область видимости блока.обзор
Область действия - это область кодовой базы, по которой действителен идентификатор.
Лексическая среда - это отображение между именами идентификаторов и значениями, связанными с ними.
Область действия формируется из связанного вложения лексических сред, причем каждый уровень вложения соответствует лексическому окружению контекста выполнения предка.
Эти связанные лексические среды образуют «цепочку». Разрешение идентификатора - это процесс поиска по этой цепочке соответствующего идентификатора.
Разрешение идентификатора происходит только в одном направлении: наружу. Таким образом, внешние лексические среды не могут «видеть» внутреннюю лексическую среду.
Есть три соответствующие факторы в определении сферы в качестве идентификатора в JavaScript:
Некоторые из способов идентификации идентификаторов:
var
,let
иconst
var
в нестрогом режиме)import
заявленияeval
Некоторые из идентификаторов местоположений могут быть объявлены:
Декларация Стили
вар
Идентификаторы, объявленные с использованием,
var
имеют область действия функции , за исключением случаев, когда они объявляются непосредственно в глобальном контексте, и в этом случае они добавляются в качестве свойств глобального объекта и имеют глобальную область действия. Существуют отдельные правила их использования вeval
функциях.пусть и const
Идентификаторы объявлены с использованием
let
иconst
имеют область видимости блока , кроме случаев, когда они объявлены непосредственно в глобальном контексте, и в этом случае они имеют глобальную область видимости.Примечание:
let
,const
иvar
все поднимают . Это означает, что их логическая позиция определения является вершиной их охватывающей области (блока или функции). Тем не менее, переменные объявлены с использованиемlet
иconst
не могут быть прочитаны или назначены, пока контроль не пройдет точку объявления в исходном коде. Промежуточный период известен как временная мертвая зона.Имена параметров функции
Имена параметров функции находятся в теле функции. Обратите внимание, что в этом есть небольшая сложность. Функции, объявленные в качестве аргументов по умолчанию, закрываются в списке параметров , а не в теле функции.
Объявления функций
Объявления функций имеют область видимости в строгом режиме и область видимости функции в нестрогом режиме. Примечание: нестрогий режим - это сложный набор возникающих правил, основанный на причудливых исторических реализациях различных браузеров.
Выражения именованных функций
Выражения именованных функций ограничены самими собой (например, с целью рекурсии).
Неявно определенные свойства глобального объекта
В нестрогом режиме неявно определенные свойства глобального объекта имеют глобальную область действия, поскольку глобальный объект находится в верхней части цепочки области действия. В строгом режиме это не разрешено.
Eval
В
eval
строках переменные, объявленные с использованием,var
будут помещены в текущую область или, еслиeval
используется косвенно, в качестве свойств глобального объекта.Примеры
Следующее вызовет ReferenceError, потому что имена
x
,y
и неz
имеют никакого значения вне функцииf
.Следующее вызовет ReferenceError для
y
иz
, но не дляx
, потому что видимостьx
не ограничена блоком. Блоки , которые определяют органы управления структур , таких какif
,for
иwhile
ведут себя аналогичным образом .Далее
x
видно за пределами цикла, посколькуvar
имеет область действия функции:... из-за этого поведения вы должны быть осторожны при закрытии переменных, объявленных
var
в циклах. Здесь объявлен только один экземпляр переменнойx
, и он логически находится вне цикла.Следующая распечатка выполняется
5
пять раз, а затем распечатывается5
в шестой раз заconsole.log
пределами цикла:Следующие распечатки,
undefined
потому чтоx
это блок-область. Обратные вызовы запускаются один за другим асинхронно. Новое поведение дляlet
переменных означает , что каждая анонимная функция сомкнулась над другой переменной с именемx
( в отличие от нее сделали бы сvar
), и так целых чисел0
через4
распечатываются .:Следующее НЕ выдает a,
ReferenceError
потому что видимостьx
не ограничена блоком; однако он напечатает,undefined
потому что переменная не была инициализирована (из-заif
оператора).Переменная, объявленная в верхней части
for
цикла при помощи,let
попадает в тело цикла:Следующее будет выбрасывать,
ReferenceError
потому что видимостьx
ограничена блоком:Переменные , объявленные с использованием
var
,let
илиconst
все области видимости модулей:Далее будет объявлено свойство для глобального объекта, потому что переменные, объявленные с использованием
var
в глобальном контексте, добавляются как свойства к глобальному объекту:let
иconst
в глобальном контексте не добавляйте свойства к глобальному объекту, но все еще имеют глобальную область видимости:Параметры функции можно считать объявленными в теле функции:
Параметры блока захвата ограничены телом блока захвата:
Выражения именованных функций ограничиваются только самим выражением:
В нестрогом режиме неявно определенные свойства глобального объекта имеют глобальную область видимости. В строгом режиме вы получаете ошибку.
В нестрогом режиме объявления функций имеют область действия. В строгом режиме они имеют блочную область видимости.
Как это работает под капотом
Область действия определяется как лексическая область кода, по которой действителен идентификатор.
В JavaScript, каждая функция-объект имеет скрытую
[[Environment]]
ссылку , которая является ссылкой на лексическую среду в контексте выполнения (кадре стеки) , в течение которого он был создан.Когда вы вызываете функцию, вызывается скрытый
[[Call]]
метод. Этот метод создает новый контекст выполнения и устанавливает связь между новым контекстом выполнения и лексической средой объекта-функции. Это делается путем копирования[[Environment]]
значения объекта-функции во внешнее поле ссылки в лексической среде нового контекста выполнения.Обратите внимание, что эта связь между новым контекстом выполнения и лексической средой объекта функции называется замыканием .
Таким образом, в JavaScript область видимости реализуется через лексические среды, связанные друг с другом в «цепочку» внешними ссылками. Эта цепочка лексических сред называется цепочкой областей действия, и разрешение идентификатора происходит путем поиска в цепочке соответствующего идентификатора.
Узнайте более .
источник
Javascript использует цепочки областей действия, чтобы установить область действия для данной функции. Обычно существует одна глобальная область, и каждая определенная функция имеет свою собственную вложенную область. Любая функция, определенная в другой функции, имеет локальную область видимости, которая связана с внешней функцией. Это всегда позиция в источнике, который определяет область действия.
Элемент в цепочке областей действия - это в основном Map с указателем на родительскую область видимости.
При разрешении переменной javascript начинается с самой внутренней области и выполняет поиск за ее пределами.
источник
Переменные, объявленные глобально, имеют глобальную область видимости. Переменные, объявленные внутри функции, относятся к этой функции и скрывают глобальные переменные с тем же именем.
(Я уверен, что есть много тонкостей, на которые настоящие программисты JavaScript смогут указать в других ответах. В частности, я наткнулся на эту страницу о том, что именно
this
означает в любое время. Надеюсь, этой более вводной ссылки достаточно, чтобы вы начали, хотя .)источник
Старая школа JavaScript
Традиционно JavaScript имеет только два типа области видимости:
Я не буду подробно останавливаться на этом, поскольку уже есть много других ответов, объясняющих разницу.
Современный JavaScript
В последние спецификации JavaScript теперь также позволяют третий объем:
Как создать переменные области видимости блока?
Традиционно вы создаете свои переменные следующим образом:
Переменные области видимости блока создаются следующим образом:
Так в чем же разница между функциональной областью и областью блока?
Чтобы понять разницу между функциональной областью и областью блока, рассмотрим следующий код:
Здесь мы видим, что наша переменная
j
известна только в первом цикле for, но не до и после. Тем не менее, наша переменнаяi
известна во всей функции.Кроме того, учтите, что переменные в области блока не известны до того, как они объявлены, потому что они не подняты. Вам также не разрешается повторно объявлять одну и ту же переменную области действия блока в одном и том же блоке. Это делает переменные области блока менее подверженными ошибкам, чем переменные глобальной или функциональной области, которые перемещаются и которые не выдают никаких ошибок в случае нескольких объявлений.
Безопасно ли сегодня использовать блочные переменные области видимости?
Насколько это безопасно сегодня или нет, зависит от вашей среды:
Если вы пишете код JavaScript на стороне сервера ( Node.js ), вы можете смело использовать это
let
утверждение.Если вы пишете код JavaScript на стороне клиента и используете браузер (например, Traceur или babel-standalone ), вы можете смело использовать это
let
утверждение, однако ваш код, вероятно, будет не оптимальным с точки зрения производительности.Если вы пишете код JavaScript на стороне клиента и используете транспортер на основе Node (например, скрипт оболочки traceur или Babel ), вы можете смело использовать это
let
утверждение. А поскольку ваш браузер будет знать только о передаваемом коде, недостатки производительности должны быть ограничены.Если вы пишете код JavaScript на стороне клиента и не используете транспортер, вам следует подумать о поддержке браузера.
Вот некоторые браузеры, которые вообще не поддерживают
let
:Как отслеживать поддержку браузера
Обновленный обзор того, какие браузеры поддерживают
let
оператор на момент прочтения этого ответа, см. На этойCan I Use
странице .(*) Глобальные и функциональные переменные могут быть инициализированы и использованы до того, как объявлены, потому что переменные JavaScript подняты . Это означает, что объявления всегда находятся на вершине области видимости.
источник
Вот пример:
Вы захотите исследовать замыкания и узнать, как использовать их для создания частных пользователей .
источник
Ключ, насколько я понимаю, состоит в том, что Javascript имеет определение уровня функции по сравнению с более распространенной областью видимости блока C.
Вот хорошая статья на эту тему.
источник
В «Javascript 1.7» (расширение Mozilla до Javascript) также можно объявить переменные области блока с помощью
let
оператора :источник
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 вводят переменные в соответствующие блоки, и это является областью видимости блоков. Эти переменные связываются со свойствами переданных в них объектов.
РЕДАКТИРОВАТЬ: Уточняющий пример:
test1 ограничен блоком with, но имеет псевдоним a.test1. «Var test1» создает новую переменную test1 в верхнем лексическом контексте (функция или глобальная переменная), если только она не является свойством a, которым она является.
Хлоп! Будьте осторожны, используя 'with' - точно так же, как var является noop, если переменная уже определена в функции, это также noop относительно имен, импортируемых из объекта! Небольшое упоминание имени, которое уже определено, сделает это намного безопаснее. Я лично никогда не буду использовать с из-за этого.
источник
with
оператор является формой видимости блока, ноcatch
предложения - гораздо более распространенная форма (забавный факт, v8 реализуетcatch
с awith
) - это практически единственные формы видимости блока в самом JavaScript (то есть функция, глобальная, try / catch , с и их производными), однако хост-среды имеют разные понятия области видимости - например, встроенные события в браузере и модуль VM NodeJS.Я обнаружил, что многие новички в JavaScript испытывают трудности с пониманием того, что наследование доступно по умолчанию в языке, и что область функций пока является единственной областью действия. Я предоставил расширение для украшения, которое я написал в конце прошлого года, под названием JSPretty. Функциональные цвета функционируют в коде области видимости и всегда связывают цвет со всеми переменными, объявленными в этой области. Закрытие визуально демонстрируется, когда переменная с цветом из одной области видимости используется в другой области видимости.
Попробуйте эту функцию на:
Смотрите демо на:
Посмотреть код на:
В настоящее время эта функция поддерживает 16 вложенных функций, но в настоящее время не окрашивает глобальные переменные.
источник
JavaScript имеет только два типа области видимости:
var
ключевым словом, имеет функциональную область.Всякий раз, когда вызывается функция, создается объект переменной области (и включается в цепочку областей действия), за которой следуют переменные в JavaScript.
Цепочка прицела ->
a
иouter
функция находятся на верхнем уровне в цепочке областей действия.variable scope object
(и включена в цепочку областей видимости), добавленную с переменнойb
внутри нее.Теперь, когда переменная
a
требуется, она сначала ищет ближайшую область видимости переменной, а если переменная отсутствует, то она перемещается к следующему объекту цепочки области видимости переменной. В данном случае это уровень окна.источник
Просто чтобы добавить к другим ответам, область действия - это список поиска всех объявленных идентификаторов (переменных) и обеспечивает строгий набор правил, касающихся того, как они доступны для выполняемого в настоящее время кода. Этот поиск может быть использован для назначения переменной, которая является ссылкой LHS (слева), или для получения ее значения, которая является ссылкой RHS (правой стороны). Эти поиски - то, что движок JavaScript делает внутренне, когда он компилирует и выполняет код.
Таким образом, с этой точки зрения, я думаю, что поможет картина, которую я нашел в электронной книге «Области применения и замыкания» Кайла Симпсона:
Цитирую из его книги:
Стоит упомянуть одну вещь: «Поиск в области видимости прекращается, когда он находит первое совпадение».
Эта идея «уровней области действия» объясняет, почему «это» можно изменить с помощью вновь созданной области действия, если она ищется во вложенной функции. Вот ссылка, которая входит во все эти детали, Все , что вы хотели знать о сфере действия JavaScript
источник
запустить код. надеюсь, что это даст представление о области видимости
источник
Глобальная сфера:
Глобальные переменные точно такие же, как глобальные звезды (Джеки Чан, Нельсон Мандела). Вы можете получить к ним доступ (получить или установить значение) из любой части вашего приложения. Глобальные функции похожи на глобальные события (Новый год, Рождество). Вы можете выполнить (вызвать) их из любой части вашего приложения.
Местный охват:
Если вы находитесь в США, вы можете знать Ким Кардашьян, печально известную знаменитость (ей каким-то образом удается создавать таблоиды). Но люди за пределами США не узнают ее. Она местная звезда, привязанная к своей территории.
Локальные переменные похожи на локальные звезды. Вы можете получить к ним доступ только (получить или установить значение) внутри области. Локальная функция похожа на локальные события - вы можете выполнять только (праздновать) внутри этой области. Если вы хотите получить к ним доступ за пределами области действия, вы получите ошибку ссылки
Проверьте эту статью для глубокого понимания области
источник
ПОЧТИ есть только два типа областей JavaScript:
Таким образом, любые блоки, кроме функций, не создают новую область видимости. Это объясняет, почему циклы for перезаписывают внешние переменные области видимости:
Вместо этого используйте функции:
В первом примере область видимости блока отсутствовала, поэтому первоначально объявленные переменные были перезаписаны. Во втором примере из-за функции появилась новая область видимости, поэтому первоначально объявленные переменные были затенены, а не перезаписаны.
Это почти все, что вам нужно знать с точки зрения JavaScript, за исключением:
Таким образом, вы можете видеть, что область видимости JavaScript на самом деле очень проста, хотя и не всегда интуитивно понятна. Несколько вещей, о которых нужно знать:
Итак, этот код:
эквивалентно:
Это может показаться нелогичным, но это имеет смысл с точки зрения императора языкового конструктора.
источник
Современные Js, ES6 +, '
const
' и 'let
'Для каждой создаваемой переменной вы должны использовать блочную область видимости, как и большинство других основных языков.
var
является устаревшим . Это делает ваш код безопаснее и удобнее в обслуживании.const
следует использовать в 95% случаев . Это делает так, чтобы ссылка на переменную не могла измениться. Свойства массива, объекта и узла DOM могут изменяться и, вероятно, должны бытьconst
.let
следует использовать для любой переменной, ожидающей переназначения. Это включает в себя цикл for. Если вы когда-либо измените значение после инициализации, используйтеlet
.Область видимости блока означает, что переменная будет доступна только в скобках, в которых она объявлена. Это распространяется на внутренние области, включая анонимные функции, созданные в вашей области.
источник
Попробуйте этот любопытный пример. В приведенном ниже примере, если бы a было числовым значением, инициализированным в 0, вы бы увидели 0, а затем 1. За исключением того, что a является объектом, и javascript передаст f1 указатель, а не его копию. В результате вы получаете одно и то же предупреждение оба раза.
источник
В JS есть только функциональные области. Не блокируйте прицелы! Вы можете увидеть, что поднимает тоже.
источник
Насколько я понимаю, есть 3 области: глобальная область, доступная глобально; локальная область, доступная для всей функции независимо от блоков; и область видимости блока, доступная только блоку, выражению или выражению, в котором он был использован. Глобальная и локальная область видимости указываются с помощью ключевого слова var как внутри функции, так и снаружи, а область видимости блока указывается с помощью ключевого слова let.
Для тех, кто считает, что существует только глобальная и локальная область действия, пожалуйста, объясните, почему у Mozilla будет целая страница, описывающая нюансы области действия блока в JS.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let
источник
Очень распространенная проблема, еще не описанная, с которой часто сталкиваются внешние кодеры, - это область видимости встроенного обработчика событий в HTML - например, с
Область действия переменных, на
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
. И эта переменная не доступна извне функции.Класс Below имеет переменную глобальной области видимости
carName
. И эта переменная доступна из любого места в классе.источник
ES5
и раньше:Переменные в Javascript изначально (до
ES6
) лексически ограничивались функциональностью. Термин лексически ограниченный означает, что вы можете увидеть область действия переменных, «посмотрев» на код.Каждая переменная, объявленная с
var
ключевым словом, попадает в область видимости функции. Однако, если в этой функции объявлена другая функция, эти функции будут иметь доступ к переменным внешних функций. Это называется цепочкой контекста . Это работает следующим образом:Пример:
Что происходит , когда мы пытаемся войти переменные
foo
,bar
иfoobar
в консоли следующее:innerFunc
. Поэтому значение foo разрешается в строкуinnerFunc
.innerFunc
. Поэтому нам нужно подняться по цепочке прицелов . Сначала мы посмотрим на внешнюю функцию, в которойinnerFunc
была определена функция . Это функцияouterFunc
. В области видимостиouterFunc
мы можем найти переменную bar, которая содержит строку «externalFunc».ES6
(ES 2015) и старше:Те же самые понятия лексического охвата и области охвата все еще применяются в
ES6
. Однако были введены новые способы объявления переменных. Есть следующее:let
: создает переменную области видимости блокаconst
: создает переменную области блока, которая должна быть инициализирована и не может быть переназначенаСамое большое различие между
var
иlet
/const
состоит в том, чтоvar
это область действия функции, тогда какlet
/const
это область действия блока. Вот пример, чтобы проиллюстрировать это:В приведенном выше примере letVar регистрирует глобальное значение, потому что переменные, объявленные с,
let
имеют область видимости. Они перестают существовать вне соответствующего блока, поэтому к переменной нельзя обратиться за пределы блока if.источник
В EcmaScript5 в основном есть две области: локальная область и глобальная область, но в EcmaScript6 у нас есть в основном три области: локальная область, глобальная область и новая область, называемая областью блока .
Пример объема блока:
источник
В ECMAScript 6 введены ключевые слова let и const. Эти ключевые слова могут использоваться вместо ключевого слова var. В отличие от ключевого слова var ключевые слова let и const поддерживают объявление локальной области видимости внутри операторов блока.
источник
Мне действительно нравится принятый ответ, но я хочу добавить это:
Scope собирает и поддерживает список поиска всех объявленных идентификаторов (переменных) и обеспечивает строгий набор правил, касающихся того, насколько они доступны для выполняемого в настоящее время кода.
Область действия - это набор правил для поиска переменных по имени их идентификатора.
источник
В JavaScript есть два типа областей действия.
Глобальная область : переменная, объявленная в глобальной области, может использоваться в любом месте программы очень плавно. Например:
Функциональная область или локальная область : переменная, объявленная в этой области, может использоваться только в своей собственной функции. Например:
источник