Область действия определяет, где переменные, функции и объекты доступны в вашем коде во время выполнения. Это означает, что область переменной (где к ней можно получить доступ) контролируется местоположением объявления переменной.
В JavaScript есть 3 типа областей видимости:
- Глобальный масштаб
- Функциональная область действия (локальная область действия)
- Область действия блока (введена в ECMAScript2015)
Глобальная область действия: Переменные известны всему приложению с самого начала.
Функциональная область: Переменные известны внутри функции, в которой они объявлены, с самого начала функции.
Область действия блока: Переменные известны в пределах блока (оператор if, оператор for), в котором они объявлены. Вы также можете использовать их отдельно с помощью простых фигурных скобок {}
Раздел I - Объем и пример выполнения:
Здесь мы обсудим, как JavaScript компилирует код и выполняет его.
var foo = 'Global_foo'; function bar () { var foo = "Local_foo"; function baz (foo) { foo = "baz_foo"; bam = "baz_bam"; } baz(); } bar(); console.log(foo); // Global_foo console.log(bam); // baz_bam baz(); // Error
В приведенном выше примере компилятор скомпилирует код следующим образом:
Компиляция:
Строка 1: компилятор разделит первую строку, поскольку компилятор 1.var foo; 2.foo = 'Global_foo'
First объявит переменную в глобальной области, а затем присвоит этой переменной значение
'Global_foo'
.
Строка 2: Компилятор сделает то же самое, что и Строка 1, он объявит имя функции bar
в глобальной области, а затем назначит тело функции этому имени.
Строка 3: компилятор разделит эту строку, поскольку 1.var foo; 2.foo = 'Local_foo'
compiler объявит переменную в области действия панели (функциональной области), а затем присвоит этой переменной значение
'local_foo'
.
Строка 5: Компилятор сделает то же самое, что и Строка 2, он объявит имя функции baz
в области панели (функциональной области), а затем назначит тело функции этому имени функции.
Поскольку одна функция baz имеет один параметр с именем foo
, компиляция объявит эту переменную в области baz (функциональная область)
Строка 6: Компилятор уже объявил переменную foo
в области baz, поэтому в этой строке компилятор присвоит значение baz_foo
переменной foo
.
* Строка 7: поскольку переменная bam
не объявлена, компилятор объявит переменную в глобальной области, а затем присвоит этой переменной значение 'baz_bam'
.
Строка 10: эта строка будет выполняться во время выполнения.
Строка 13: эта строка будет выполняться во время выполнения.
Строка 14: эта строка будет выполняться во время выполнения.
Строка 15: эта строка будет выполняться во время выполнения.
Строка 16: эта строка будет выполняться во время выполнения.
Интерпретация:
Строка 13: В этой строке Интерпретатор запросит глобальную область видимости: «Привет, глобальная область, у меня есть ссылка RHS (правая сторона) под названием bar, вы слышали об этом», затем global scope ответит: «Да, я зарегистрировал его раньше, и его значение является функцией»
Итак, Interpreter запустит эту функцию.
В строке 10 «Привет, Local» scope У меня есть ссылка на RHS (правая сторона) под названием baz, вы слышали об этом », тогда глобальная область ответит « Да, я зарегистрировал ее раньше, и ее значение - функция »
Строка 14: Интерпретатор проверит foo
variable в глобальной области видимости и напечатает global_foo
Строка 15: Интерпретатор проверит bam
variable в глобальной области (поскольку компилятор уже объявил переменную в глобальной области в строке 7) и напечатает baz_bam
Строка 16: Эта строка выдаст ошибку Uncaught Reference-error: baz is not defined
, поскольку имя функции baz
не объявлено в глобальной области, она объявлена в функциональной области, и мы вызываем ее в глобальной области
Раздел II - Объем блока
В JavaScript до ECMAScript2015 не было области блока.
Области блока - это то, что вы получаете, когда используете операторы if, for и тому подобное. Вы также можете использовать их отдельно с простыми фигурными скобками {}, которые не следует путать с пустыми объектными литералами.
{ var a } // undefined object in a block scope if (3 == '3') { // block scope for the if statement } for (var i=0; i<10; i++) { // block scope for the for statement }
ECMAScript2015 представил два новых ключевых слова, касающихся области видимости:
- позволять
- const
var vs. let
Как обсуждалось в разделе 1, ключевое слово var ведет себя по-разному в областях действия функций и блоках. Переменная, объявленная с помощью var в области действия функции, недоступна вне области действия функции.
function funcScope() { var secret = 42; } secret; // ReferenceError: secret is not defined (in this scope i.e global scope)
Переменная, объявленная с помощью var в области блока, доступна за пределами области действия этого блока.
for(var i = 0; i < 10; i++) { // block scope } console.log(i) // => 10
Переменные, объявленные с помощью let внутри области видимости блока, доступны только внутри этой области. Если мы используем let для объявления переменной i в цикле for, эта переменная будет доступна только внутри цикла for.
for (let i=0; i<10; i++) { // block scope for the for statement } console.log(i) // ReferenceError: i is not defined
const
Объявление переменной с помощью const в точности похоже на let - когда дело доходит до области действия - но создает постоянную ссылку для переменной. Мы не можем изменить значение постоянной ссылки. Если мы поместим примитивное значение в константу, это значение будет защищено от изменения:
const PI = 3.141592653589793 PI = 42 // Error: Assignment to constant variable i.e "PI" is read-only
Обратите внимание, что если константа является объектом, мы все равно можем изменить свойства этого объекта, поэтому будьте осторожны:
const sports = { name: 'Cricket' }; sports.name = 'Football'; console.log(sports.name)
Однако мы не можем переназначить объект, объявленный с помощью const:
const sports = { name: 'Cricket' }; const sports = { name: 'Football' }; // SyntaxError: Identifier 'sports' has already been declared
Константы часто используются при импорте вещей из других библиотек, чтобы их случайно не изменить. Например, в Node.js мы используем его с функцией require:
const R = require('ramda');
Раздел III - Лексическая область видимости
JavaScript имеет лексическую область видимости с областью действия. Другими словами, хотя JavaScript выглядит так, как будто он должен иметь область видимости блока, потому что он использует фигурные скобки {}, новая область создается только тогда, когда вы создаете новую функцию.
var outerFunction = function(){ if(true){ var x = 5; //console.log(y); //line 1, ReferenceError: y not defined } var nestedFunction = function() { if(true){ var y = 7; console.log(x); //line 2, x will still be known prints 5 } if(true){ console.log(y); //line 3, prints 7 } } return nestedFunction; } var myFunction = outerFunction(); myFunction();
В этом примере переменная x доступна везде внутри outerFunction()
. Кроме того, переменная y доступна везде в пределах nestedFunction()
, но ни одна из них не доступна за пределами функции, в которой они были определены. Причину этого можно объяснить с помощью лексической области видимости. Объем переменных определяется их положением в исходном коде. Чтобы разрешить переменные, JavaScript начинает с самой внутренней области видимости и ищет снаружи, пока не найдет искомую переменную. Лексическая область видимости - это хорошо, потому что мы можем легко выяснить, каким будет значение переменной, посмотрев на код; тогда как в динамической области видимости значение переменной может меняться во время выполнения, что усложняет задачу.
Обман лексической области: eval
eval можно использовать для обмана с лексической областью видимости
var bar = 'bar'; function foo (str) { eval(str); console.log(bar); // 42 } foo('var bar = 42');
Ожидаемый результат в строке 3 - bar
. Но поскольку мы использовали eval, eval вычислит выражение и получит результат как 42
Но использование функции eval вредно, так как eval выполняется во время выполнения и из-за этого влияет на производительность.
Раздел IV - Подъем
Объявления функций и функциональные переменные всегда перемещаются («поднимаются») в верхнюю часть области видимости JavaScript интерпретатором JavaScript.
Начнем с примеров:
function foo(){ function bar() { return 3; } return bar(); function bar() { return 8; } } alert(foo());
Когда объявление функции поднимается (т.е. когда функция вызывается перед ее объявлением), все тело функции поднимается вместе с ней, поэтому после того, как интерпретатор завершил работу с приведенным выше кодом, он работает примерно так:
//**Simulated processing sequence for Question 1** function foo(){ //define bar once function bar() { return 3; } //redefine it function bar() { return 8; } //return its invocation return bar(); //8 } alert(foo());
Поднимаются ли и функциональные выражения?
function foo(){ var bar = function() { return 3; }; return bar(); var bar = function() { return 8; }; } alert(foo());
Это зависит от выражения. Давайте посмотрим на первое выражение в приведенном выше коде.
var bar = function() { return 3; };
Левая часть (var bar) - это объявление переменной. Объявления переменных поднимаются, а выражения присваивания - нет. Поэтому, когда bar поднимается, интерпретатор изначально устанавливает var bar = undefined. Само определение функции не поднимается.
Таким образом, приведенный выше код работает в более интуитивной последовательности:
//**Simulated processing sequence for Question 2** function foo(){ //a declaration for each function expression var bar = undefined; var bar = undefined; //first Function Expression is executed bar = function() { return 3; }; // Function created by first Function Expression is invoked return bar(); // second Function Expression unreachable } alert(foo()); //3
Теперь приведенный ниже код кажется простым. Здесь нет функции подъема…
function foo(){ return bar(); var bar = function() { return 3; }; var bar = function() { return 8; }; } alert(foo());
Таким образом, приведенный выше код работает в более интуитивной последовательности:
//**Simulated processing sequence for Question 4** function foo(){ //a declaration for each function expression var bar = undefined; var bar = undefined; return bar(); //TypeError: "bar is not a function" //neither Function Expression is reached } alert(foo());
Итак, если мы изменим строку 2, например, return bar
, то вывод будет undefined
, потому что объявления переменных поднимаются, а их выражения присваивания - нет. Поэтому, когда bar поднимается, интерпретатор изначально устанавливает var bar = undefined.
Но в случае return bar()
интерпретатор выдаст ошибку как TypeError: "bar is not a function"
, потому что сейчас значение bar не определено.
Что следует помнить о подъеме
- Если вы определяете функцию с помощью декларации функции, вы можете вызывать эту функцию в любом месте в пределах текущей области.
- Если вы определяете функцию с помощью Function Expression, вы должны вызывать эту функцию после объявления в текущей области.
Если вы хотите узнать больше об объявлении функции и выражении функции, посетите следующую статью:
https://medium.com/@bharat.suryawanshi2010/functions-key-thing-of-javascript-83926f2c5dd
Спасибо за чтение :)