Область действия определяет, где переменные, функции и объекты доступны в вашем коде во время выполнения. Это означает, что область переменной (где к ней можно получить доступ) контролируется местоположением объявления переменной.

В JavaScript есть 3 типа областей видимости:

  1. Глобальный масштаб
  2. Функциональная область действия (локальная область действия)
  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 представил два новых ключевых слова, касающихся области видимости:

  1. позволять
  2. 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

Спасибо за чтение :)