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

Переменный объект

Когда выполняется функция JavaScript, помимо уже созданного глобального контекста выполнения, создается контекст выполнения, связанный с функцией. Переменный объект - это просто объект, хранящий данные, относящиеся к контексту выполнения. Данные включают переменные и объявления функций, определенные в контексте. Рассмотрим следующий пример:

function foo(){
    var a = 20;     
    function bar(){}
    (function qux(){});
    console.log(a);     // 20
    console.log(bar);   // function bar(){}
    console.log(qux);   // reference error
}
foo();

В приведенном выше примере объект переменной функции foo () содержит объявление переменной a и функции bar (). Здесь следует отметить, что, хотя объявление функции включено в объект переменной, выражение функции не, как мы видим в примере, оно показывает, что доступ к qux () приводит к ошибке ссылки. Переменный объект является абстрактным и особенным, к нему нельзя получить доступ в коде, но он обрабатывается движком JavaScript.

Объект активации

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

function foo(x, y){
    var a = 20;     
    function bar(){}
    (function qux(){});    
}

В приведенном выше примере объект активации функции foo () содержит объект arguments, параметр x, y, переменную a , функция bar (). Как и в случае с переменным объектом, функция qux () не включена в объект активации.

Цепочка областей действия

Цепочка областей видимости - это серия областей видимости или переменных объектов, определяющих, к каким переменным и функциям имеет доступ определенный контекст выполнения. Давайте посмотрим на пример:

var a = 10;
function foo(){
    var b = 12;
    var c = addANumber(20);
    function addANumber(num){
        return a + num;
    }
    console.log(c);     // 30
}
foo();

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

В приведенном выше примере есть три контекста выполнения. Глобальный контекст выполнения, содержащий переменную a и функцию foo (), контекст выполнения функции foo (), содержащий переменную b, c и addANumber () и, наконец, контекст выполнения функции addANumber (), которая содержит параметр num. Помимо переменных b, c и функции addANumber (), контекст функции foo () также имеет доступ к переменной a глобального контекста. Точно так же, помимо переменной num, контекст функции addANumber () также имеет доступ к переменной b, c функции foo () и переменной a глобального контекста. Однако глобальный контекст не имеет доступа к контексту foo (), а контекст foo () не имеет доступа к контексту addANumber (). Короче говоря, внутренний контекст имеет доступ ко всему своему внешнему содержащему контексту, но внешний контекст не имеет доступа ни к одному из своих внутренних контекстов.

Процесс поиска идентификаторов в функции начинается с объекта активации функции. Когда функция ссылается на идентификатор, который может быть переменной, объявлением функции или формальным параметром, идентификатор ищется в текущем контексте выполнения, если он не может быть найден, тогда он будет найден в содержащем контексте вплоть до цепочки областей видимости. Когда достигнут конец цепочки областей видимости и идентификатор не может быть найден, он будет считаться неопределенным. В приведенном выше примере, когда переменная a упоминается в функции addANumber (), переменная будет просматриваться в текущем контексте выполнения функции addANumber (), здесь она не может быть найдена, тогда она будет искать в контексте выполнения функции foo (), ее также здесь нет, наконец, она находится в глобальном контексте.

Следующая картинка объединяет все воедино:

Хотя разработчики не работают напрямую с этими концепциями, важно глубоко понимать их в деталях.