12 KiB
		
	
	
	
	
	
	
	
			
		
		
	
	title, localeTitle
| title | localeTitle | 
|---|---|
| Scopes | Области применения | 
Если вы некоторое время программируете в JavaScript, вы, несомненно, сталкиваетесь с концепцией, известной как scope . Что такое scope ? Зачем вам тратить время на это?
Говоря программистом, scope - это текущий контекст исполнения . Смущенный? Давайте рассмотрим следующий фрагмент кода:
var foo = 'Hi, I am foo!'; 
 
 var baz = function () { 
  var bar = 'Hi, I am bar too!'; 
    console.log(foo); 
 } 
 
 baz(); // Hi, I am foo! 
 console.log(bar); // ReferenceError... 
Это простой пример, но он хорошо иллюстрирует то, что известно как лексический охват . JavaScript, и почти любой другой язык программирования имеет лексический охват . Существует еще один вид области действия, называемый динамическим масштабом , но мы не будем обсуждать это.
Теперь термин Lexical scope кажется фантастическим, но, как вы увидите, в принципе это очень просто. В Лексическом Области есть два вида областей: глобальная область действия и локальная область .
Прежде чем вводить первую строку кода в свою программу, для вас создается глобальная область . Это содержит все переменные, которые вы заявляете в своей программе вне любых функций .
В приведенном выше примере переменная foo находится в глобальной области действия программы, в то время как переменная bar объявляется внутри функции и, следовательно, находится в локальной области этой функции .
Давайте разложим пример строки за строкой. Хотя вы можете быть смущены в этот момент, я обещаю, что к тому времени, когда вы это прочитаете, у вас будет гораздо лучшее понимание.
В строке 1 мы объявляем переменную foo . Здесь нет ничего необычного. Давайте назовем эту ссылку левым (LHS) ссылкой на foo , потому что мы назначаем значение foo и оно находится в левой части знака equal .
В строке 3 мы объявляем функцию и присваиваем ее переменной baz . Это еще одна ссылка LHS на baz . Мы присваиваем ему значение (помните, что функции тоже являются значениями!). Затем эту функцию вызывают в строке 8. Это RHS или правая ссылка на baz . Мы извлекаем значение baz , которое в этом случае является функцией и затем вызывает ее. Другая ссылка RHS на baz была бы, если бы мы присвоили ее значение другой переменной, например foo = baz . Это будет ссылка LHS на foo и ссылку RHS на baz .
Ссылки LHS и RHS могут показаться запутанными, но они важны для обсуждения области. Подумайте об этом так: ссылка LHS присваивает значение переменной, в то время как ссылка RHS возвращает значение переменной. Это всего лишь более короткий и удобный способ сказать «получение значения» и «присвоение значения».
Давайте теперь разбиваем, что происходит внутри самой функции.
Когда компилятор компилирует код внутри функции, он входит в локальную область функции .
В строке 4 объявляется переменная bar . Это ссылка LHS на bar . На следующей строке мы имеем ссылку RHS для foo внутри console.log() . Помните, что мы извлекаем значение foo , а затем передаем его в качестве аргумента в метод console.log() .
Когда у нас есть ссылка RHS на foo , компилятор ищет объявление переменной foo . Компилятор не находит его в самой функции или локальной области функции, поэтому она поднимается на один уровень: в глобальную область .
На данный момент вы, вероятно, думаете, что область действия имеет какое-то отношение к переменным. Это верно. Область может рассматриваться как контейнер для переменных. Все переменные, созданные в локальной области, доступны только в этой локальной области. Однако все локальные области доступа могут получить доступ к глобальной области. (Я знаю, что вы, вероятно, еще больше запутались прямо сейчас, но просто несите меня еще несколько абзацев).
Таким образом, компилятор переходит в глобальную область, чтобы найти ссылку LHS на переменную foo . Он находит один в строке 1, поэтому он извлекает значение из ссылки LHS, которая представляет собой строку: 'Hi, I am foo!' , Эта строка отправляется в метод console.log() и выводится на консоль.
Компилятор завершил выполнение кода внутри функции, поэтому мы возвращаемся к строке 9. В строке 9 мы имеем ссылку RHS для переменной bar .
Теперь, bar был объявлен в локальном объеме baz , но есть ссылка RHS для bar в глобальном масштабе. Поскольку в глобальной области нет ссылки LHS для bar , компилятор не может найти значение для bar и выбрасывает ReferenceError.
Но вы можете спросить, может ли функция выглядеть вне себя для переменных или локальная область может заглянуть в глобальную область, чтобы найти ссылки LHS, почему глобальная область не может заглянуть в локальную область? Ну, вот как работает лексический охват!
... // global scope 
 var baz = function() { 
  ... // baz's scope 
 } 
 ... /// global scope 
Это тот же код сверху, который иллюстрирует область действия. Это создает иерархию, которая поднимается до глобальной области:
baz -> global .
Итак, если есть ссылка RHS для переменной внутри области baz , она может быть выполнена с помощью ссылки LHS для этой переменной в глобальной области. Но противоположное не соответствует действительности .
Что, если бы у нас была другая функция внутри baz ?
... // global scope 
 var baz = function() { 
  ... // baz's scope 
 
  var bar = function() { 
     ... // bar's scope. 
  } 
 
 } 
 ... /// global scope 
В этом случае иерархия или цепочка областей будут выглядеть так:
bar -> baz -> global
Любые ссылки RHS внутри локальной области bar могут быть заполнены ссылками LHS в глобальном масштабе или в области baz , но ссылка RHS в области baz не может быть заполнена ссылкой LHS в области bar .
Вы можете перемещаться по цепочке видимости, а не вверх.
Есть еще две важные вещи, которые вы должны знать о облаках JavaScript.
- Объекты объявляются функциями, а не блоками.
- Функции могут быть переданы по прямой ссылке, переменные не могут.
Наблюдайте (каждый комментарий описывает область действия в строке, на которой она написана):
`` ` // outer () здесь в области видимости, поскольку функции могут быть переданы по прямой ссылке
function outer() { 
 
    // only inner() is in scope here 
    // because only functions are forward-referenced 
 
    var a = 1; 
 
    //now 'a' and inner() are in scope 
 
    function inner() { 
        var b = 2 
 
        if (a == 1) { 
            var c = 3; 
        } 
 
        // 'c' is still in scope because JavaScript doesn't care 
        // about the end of the 'if' block, only function inner() 
    } 
 
    // now b and c are out of scope 
    // a and inner() are still in scope 
 
 } 
 
 // here, only outer() is in scope 
`` `
Рекомендации
- Области и закрытие Кайла Симпсона. В нем подробно описывается, как работает механизм видимости, а также имеет поверхностное описание того, как работает компилятор JavaScript, поэтому, если вас это интересует, обязательно дайте ему прочитать! Это бесплатно на GitHub и можно купить у O'Reilly.
- Секреты JavaScript ниндзя Джона Ресига и Медведя Бибо. Отличное руководство для более глубокого понимания JavaScript.