Углубленная серия JavaScript (1): лексическое окружение

ECMAScript 6

1. Лексическое окружение

Лексическое окружение описано в спецификации ECMAScript следующим образом.: Лексическое окружение — это тип спецификации, используемый для определения ассоциации между идентификаторами и значениями переменных и функций в коде ECMAScript на основе лексически вложенных структур. Лексическое окружение состоит из записи окружения и, возможно,nullОтношение к внешнему лексическому окружению (внешнему) составу. Как правило, лексические среды связаны с определенными синтаксическими структурами кода ECMAScript, такими как функции, блоки кода,TryCatchсерединаCatchпредложение, и при каждом выполнении такого кода создается новое лексическое окружение.

вкратце,Лексическое окружениев соответствующем блоке кодаАссоциация идентификатор-значениепроявление. Если вы знали раньшеобъемпонятийные слова иЛексическое окружениепохож (после ES6объемконцепция становитсяЛексическое окружениеконцепция).

Лексическое окружение состоит из двух компонентов:

  1. Запись окружающей среды: записывает привязку идентификатора для соответствующего блока кода.

    Можно понять, что все объявления переменных и объявления функций в соответствующем блоке кода (если блок кода является функцией, также включает ее формальные параметры) здесь
    Соответствует переменному объекту или активному объекту до ES6, те, кто не понимает, могут быть проигнорированы

  2. Отношение к внешнему лексическому окружению (внешнему): Используется для формирования логически вложенной структуры из нескольких лексических сред для обеспечения возможности доступа к внешним переменным лексических сред.

    Логически вложенная структура лексического окружения соответствует цепочке областей действия до ES6, которую можно игнорировать, если вы ее не понимаете.

2. Экологическая запись

Существует три типа экологических записей, а именно:Декларативная запись среды,Запись среды объекта,Глобальный экологический рекорд.

1. Декларативная запись среды

Записи декларативной среды используются для определения элементов синтаксиса ES, которые напрямую связывают идентификаторы со значениями языка, такими как объявления переменных, констант, let, class, module, import и function.

Существует два специальных типа записей декларативной среды: запись среды функции и запись среды модуля.

1.1 Запись функциональной среды

Запись среды функции используется для представления области действия функции верхнего уровня, и если функция не является стрелочной функцией, она также предоставляетthisпривязка.

1.2 Запись среды модуля

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

2. Запись среды объекта

Каждая запись объектной среды связана с объектом, называемым записью объектной среды.binding object. Можно понять, что объектно-ориентированная запись среды основана на этомbinding object, привязка идентификатора выполняется в виде свойства объекта, и идентификатор совпадает сbinding objectИмена атрибутов взаимно однозначного соответствия.

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

Записи среды объекта используются для определения элементов синтаксиса ES, которые связывают идентификаторы с определенными свойствами объекта, такими как операторы, объявления глобальных переменных и объявления функций.

3. Глобальный экологический рекорд

Запись глобальной среды логически представляет собой одну запись, но на самом деле ее можно рассматривать как对象式环境记录компоненты и声明式环境记录Инкапсуляция компонентов.

Я сказал перед каждым对象式环境记录Имеетсяbinding object, записанный глобальной средой对象式环境记录изbinding objectЯвляется ли глобальный объект в браузере глобальнымthisа такжеwindowВсе привязки указывают на глобальный объект.

глобальный экологический рекорд对象式环境记录Компонент, который связывает все встроенные глобальные свойства, глобальные функции объявления и глобальныеvarутверждение.

Итак, эти привязки мы можем пройтиwindow.xxилиthis.xxполученный.

Другие объявления глобального кода (например,пусть, const, класси т. д.) связаны в声明式环境记录компоненты, так как声明式环境记录Компоненты не реализуют привязки на основе простой формы объекта, поэтому эти объявления мы не делаем.нельзя получить доступ через свойства глобального объекта.

3. Отсылки к внешнему лексическому окружению (внешнему)

В первую очередь следует отметить два момента:

  1. Ссылка на внешнее лексическое окружение глобального окружения:null.
  2. Лексическое окружение может служить внешней средой для нескольких лексических окружений. Например, если несколько функций объявлены глобально, все ссылки на внешнее лексическое окружение этих лексических окружений функций указывают на глобальное окружение.

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

Существование цепочки окружения предназначено для разбора идентификаторов, то есть поиска переменных с точки зрения непрофессионала. Во-первых, ищите переменные в текущей среде. Если вы не можете их найти, перейдите во внешнюю среду, чтобы найти их. Если вы не можете их найти, перейдите во внешнюю среду внешней среды, чтобы найти их, и так пока не найдёте их, или перейдёте на вершину цепочки окружения (глобальное окружение), то выкидывайте ихReferenceError.

Разрешение идентификатора: процесс разрешения переменных (привязок) в цепочке окружения,

Мы используем псевдокод для имитации процесса разбора идентификатора.

ResolveBinding(name[, LexicalEnvironment]) {
    // 如果传入词法环境为null(即一直解析到全局环境还未找到变量),则抛出ReferenceError
    if (LexicalEnvironment === null) {
        throw ReferenceError(`${name} is not defined`)
    }
    // 首次查找,将当前词法环境设置为解析环境
    if (typeof LexicalEnvironment === 'undefined') {
        LexicalEnvironment = currentLexicalEnvironment
    }
    // 检查环境的环境记录中是否有此绑定
    let isExist = LexicalEnvironment.EnviromentRecord.HasBinding(name)
    // 如果有则返回绑定值,没有则去外层环境查找
    if (isExist) {
        return LexicalEnvironment.EnviromentRecord[name]
    } else {
        return ResolveBinding(name, LexicalEnvironment.outer)
    }
}

4. Анализ случая

Выше было сказано так много теоретических знаний, а теперь рассмотрим их в сочетании с кодом.Существуют следующие глобальные коды:

var x = 10
let y = 20
const z = 30
class Person {}
function foo() {
    var a = 10
}
foo()

Теперь у нас есть глобальное лексическое окружение и лексическое окружение функции foo (Следующее содержимое представляет собой абстрактный псевдокод):

// 全局词法环境
GlobalEnvironment = {
    outer: null, // 全局环境的外部环境引用为null
    // 全局环境记录,抽象为一个声明式环境记录和一个对象式环境记录的封装
    GlobalEnvironmentRecord: {
        // 全局this绑定值指向全局对象,即ObjectEnvironmentRecord的binding object
        [[GlobalThisValue]]: ObjectEnvironmentRecord[[BindingObject]],
        // 声明式环境记录,全局除了函数和var,其他声明绑定于此
        DeclarativeEnvironmentRecord: {
            y: 20,
            z: 30,
            Person: <<class>>
        },
        // 对象式环境记录的,绑定对象为全局对象,故其中的绑定可以通过访问全局对象的属性来获得
        ObjectEnvironmentRecord: {
            // 全局函数声明和var声明
            x: 10,
            foo: <<function>>,
            // 内置全局属性
            isNaN: <<function>>,
            isFinite: <<function>>,
            parseInt: <<function>>,
            parseFloat: <<function>>,
            Array: <<construct function>>,
            Object: <<construct function>>
            // 其他内置全局属性不一一列举
        }
    }
}

// foo函数词法环境
fooFunctionEnviroment = {
    outer: GlobalEnvironment, // 外部词法环境引用指向全局环境
    FunctionEnvironmentRecord: {
        [[ThisValue]]: GlobalEnvironment, // foo函数全局调用,故this绑定指向全局环境
        // 其他函数代码内的绑定
        a: 10
    }
}

Пять, глобальное разрешение идентификатора

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

function GetGlobalBingingValue(name) {
    // 全局环境记录
    let rec = Global Environment Record
    // 全局环境记录的声明式环境记录
    let DecRec = rec.DeclarativeRecord
    // HasBinding用来检查环境记录上是否绑定给定标识符
    if (DecRec.HasBinding(name) === true) {
        return DecRec[name]
    }
    let ObjRec = rec.ObjectRecord
    if (ObjRec.HasBinding(name) === true) {
        return ObjRec[name]
    }
    throw ReferenceError(`${name} is not defined`)
}

Вы можете видеть, что при чтении глобальных переменных сначала извлекается декларативная запись среды, а затем извлекается запись объектной среды. Это приводит к некоторым интересным явлениям:

let,const,classи т.д. Если объявленная переменная существует с тем же именемvarЕсли вы объявите переменную или одноименную функцию, будет сообщено об ошибке (подробно будет описано в следующей статье). Но если мы используемlet,const,classОбъявите переменную, а затем напрямую добавьте свойство с тем же именем в глобальный объект, вы можете обойти эту ошибку.

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

серия статей

Я готов переписать ранее написанную часть статьи по ECMAScript, углубить свое понимание, сделать содержание более цельным, а структуру каталогов более разумной.

Подробный адрес каталога серии ECMAScript (постоянно обновляется...)

Добро пожаловать на чтение серии статей. Если вам нравится или у вас есть вдохновение, добро пожаловать на звезду, что также является поощрением для автора.

Новичок, если у вас есть какие-либо вопросы или вы найдете ошибки, вы можете задавать вопросы или опечатки в соответствующих выпусках и продвигаться вперед вместе со всеми.