И снова здраствуйте! Мы уже писали о том, что в конце сентября в OTUS стартует новый поток курса «Fullstack разработчик JavaScript». В преддверии начала занятий продолжаем делиться с вами авторскими статьями, подготовленными специально для студентов курса. Сегодня разберем виды контекста в JavaScript. Поехали.

Автор статьи: Павел Якупов




Цель данной статьи состоит в том, чтобы читатель понял основные концепции областей видимости, которые используются в JavaScript.

Область видимости одна из самых важных вещей в JavaScript (и имеет место быть в большинстве современных языков программирования). Область видимости(Scope) связана с длинной жизни переменной или функции, доступом, видимостью переменных, и некоторыми другими вещами.

Зачем вообще нам нужна это область видимости?


Область видимости в языках программирования выполняет следующие функции:
Безопасность(инкапсуляция) — переменные и функции доступны только когда они понадобятся.
Устраняет проблему конфликта имен переменных. Наличие областей видимости позволяет не «сваливать» в одну кучу все переменные, симулируя пространства имен(namespace).
Реиспользование кода — написанный код можно потом использовать, избегая «посторонних» эффектов.

Типы областей видимости


На самом простом уровне, в JavaScript существует две области видимости — локальная и глобальная.

В этой статье мы также затронем такую область видимости, как лексическая и блочная.

Глобальная область видимости


Когда вы открываете документ в JavaScript и начинаете писать код, вы попадаете в глобальную область видимости.

Все что создается в глобальной области видимости — переменные, функции — доступны из любой точки в программе. Также глобальные переменные доступны все время работы вашего приложения и удаляются только когда программа заканчивает свою работу.

Многие новички поначалу слишком часто используют глобальные переменные — так программировать несколько проще, однако это считается плохой практикой, и часто приводит к нестабильно работающим программам, которые в итоге используют куда больше памяти, чем им на самом деле нужно. Ведь если бы переменные были бы надежно инкапсулированы внутри функций, в которых они используются, по окончанию работы функции они были бы удалены из памяти с помощью сборщика мусора( Garbage collector), и прекратили занимать клиентскую память, которая не бесконечная.

Локальная область видимости


Переменные, которые объявлены локально, доступны только в той области видимости, где они были объявлены.

Самый простой способ создать новую область видимости — это создать новую функцию. Переменная, созданная внутри функции, доступна только изнутри. Кроме того, локальную область видимости можно получить с помощью блочной области видимости(будет разобрано ниже).

function foo(){
  let x = 15; }
//console.log(x);  error
{
  let y = 14; }
// console.log(y);  тоже error
{
  var z = 13; }
console.log(z); //опять var нам смешивает карты,
//потому что на нем не срабатывает блочная область 
//видимости   - теперь 13

Лексическая область видимости


Итак, что такое лексическая область видимости? Выражаясь простым языком — это способность внутренней функции получать доступ к внешней области видимости. Здесь стоит обратиться к практике замыканий. О них, наверное, можно написать еще пару статей, но я приведу быстро один классический пример:

sum(5)(5) //как сделать чтобы это работало?
function sum(a){
 var add = function(b){
    return a+b;
  }
 return add;
}
console.log(sum(5)(5)); // 10 возможно надо
 //отправиться в Хогвартс чтобы это понять

Блочная область видимости


Пока что мы только обсудили области видимости, которые связаны с работой функций и фигурных скобок{}, а различия в работе var и let мы обсудили только косвенно.

Как работает директива var? При объявлении переменной с её помощью в глобальной области видимости имя переменной приписывается как свойство глобальному объекту window(если мы подразумеваем браузер) и остается там все время работы программы. В тоже время как блочная область видимости, такая как {}( и куда вполне логично включаются и if, for, while и все остальные).

Кроме того, есть еще одна особенность let и const — объявленные в одной области видимости, они потом не могут быть объявлены еще раз в этой же( ну недовольство интерпретатора здесь выглядит вполне логично).

let x = 15;
console.log(x); // все отлично
{
let  x = 16; // о все по прежнему работает хорошо
console.log(x) // потому как это разные области видимости все работает нормально
let x = 17; // вот теперь у нас интерпретатор сообщит об ошибке, потому что мы переопределили переменную в этой области
}

new Function


Особенности областей видимости при объявлении новой функции как «new Function». Данный вариант создания функции используется достаточно редко, но иногда это может потребоваться.
Пример синтаксиса:

//let newFunc = new Function([arg1, arg2…argN], functionBody);
let mult = new Function(‘a’, ‘b’, ‘return a * b’);
console.log(mult(3,4));

Обычно функция запоминает, где она родилась(Lexical Environment), но когда функция создается с использованием конструкции new Function, в её переменные окружения записываются не окружающие ее переменные, как в обычной ситуации, а только объявленные глобально.

//здесь снова будут замыкания, так что надеюсь вас две ()() подряд уже не удивляют
let a = 3;
function outerFunc() {
  var a = 2;
  var func = new Function('console.log(a*a)');
  return func;
}
outerFunc()(); // 9, из глобального объекта window

Hoisting(поднятие переменных)


Обсуждая области видимости, мы не могли с вами не коснуться темы поднятия области переменных. Интерпретатор, который считывает код, на самом деле читает его два раза: он читает функции, объявленные как function declaraton, и читает глобальные переменных, объявленные глобальные с помощью переменной var. Однако переменным записывается не их объявленные значения, а значение undefined.

console.log(x); // undefined
var x = 15;
console.log(y);// error
let y = 13;

Пускай эта тема относится только косвенно к областям видимости, но иногда эти знания могут пригодиться.

Всем спасибо! Надеюсь, кому-то эта статья была полезной!



Полезные ссылки:

developer.mozilla.org/en-US/docs/Glossary/Scope
developer.mozilla.org/ru/docs/Web/JavaScript/Closures
2ality.com/2015/02/es6-scoping.html
learn.javascript.ru/new-function
habr.com/ru/company/otus/blog/466873

Комментарии (4)


  1. Aingis
    16.09.2019 18:42
    +1

    Тема сто раз пережёвана, в 2019 можно было бы и затронуть ES-модули, например.
    sum(5)(5) и new Function() — это конечно интересно, но не то, что нужно в реальном работающем коде.
    Если уж говорить про такие тонкости, то тема с eval не раскрыта, а она может как учитывать область видимости, так и нет, в зависимости от того, как вызывать.
    Вообще контекстом обычно называют другую штуку. Ту, которая про this.


  1. funca
    16.09.2019 20:34

    Как работает директива var? При объявлении переменной с её помощью в глобальной области видимости имя переменной приписывается как свойство глобальному объекту window(если мы подразумеваем браузер)

    В браузере var работает немного не так — свойства объектов живут в параллельной вселенной цепочек прототипов. Тема классно описана у http://dmitrysoshnikov.com/ecmascript/javascript-the-core/#execution-context-stack


  1. Nookie-Grey
    16.09.2019 22:16

    Я уж думал что-то новое или интересное, ну или хотя бы что-то глубокое…


  1. serf
    17.09.2019 09:15

    А куда «with» дели то?