
Область действия определяет, где переменные, функции и объекты доступны в вашем коде во время выполнения. Это означает, что область переменной (где к ней можно получить доступ) контролируется местоположением объявления переменной.
В 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
Спасибо за чтение :)