JavaScript 词法环境

388 阅读4分钟

词法环境 (LexicalEnvironment)

A Lexical Environment is a specification type used to define the association of identifiers to specific variables and functions based upon the lexical nesting structure of ECMAScript code.

简单来说就是用来定义 基于代码嵌套结构记录标识符和变量值和函数的映射关系

  • 标识符: 变量名或函数名
  • 变量: 具体变量的原始值或对象和函数的引用

词法环境通常由一下三部分组成:

  • 环境记录EnvironmentRecord:对应的代码块中的变量声明、函数声明、函数形参,对应 VO/AO
  • 对外部环境的引用outer:多个词法环境的嵌套结构,实现访问外部词法环境的能力,对应 作用域链
  • this 绑定:确认当前词法环境中 this 的指向

环境记录(EnvironmentRecord)

1. 声明式环境记录(Declarative Environment Record)

用来定义那些将标识符与值绑定的 ES 语法,例如:变量 常量 let const class module import 函数声明

1.1 函数环境记录(Function Environment Record)

每一次调用时会产生函数环境记录,并且会有 this 指向的绑定

1.2 模块环境记录(Module Environment Record)

体现一个模块的外部作用域 export ,并且提供外部引入的模块的绑定 import

2. 对象式环境记录(Object Environment Record)

每一个对象环境记录都有一个与之相关联的对象 binding object ,以对象属性的形式进行标识绑定

3. 全局式环境记录(Global Environment Record)

全局环境记录声明式环境记录 对象环境记录 以及对外部环境的引用 outer(null)
全局环境记录对象环境记录binding object 就是全局对象,在浏览器里 thiswindow 都能访问到这个对象
全局环境记录对象环境记录 绑定了所有 内置全局属性 全局函数声明全局 var 声明,并且有一个 global binding object(binding object) 与之关联

通过下面的案例我们可以通过伪代码抽象出一个词法环境

var name = 'xiaoming';
window.age = 18;
function listen() {
    var text = 'hi';
}
let say = function () {};
const eat = function () {};
GlobalEnvironment = {
    // 全局环境的外部环境引用为 null
    outer: null,
    // 全局环境记录, 封装声明式环境记录和对象环境记录
    GlobalEnvironmentRecord: {
        // global binding object, 即全局环境的 binding object, this 指向为全局对象
        [[GlobalThis]]: Global,
        // 声明式环境记录, let const 等都保存在这里
        DeclarativeEnvironmentRecord: {
            say: <function>,
            eat: <function>
        },
        // 对象环境记录, 全局环境记录下绑定为全局对象, 全局声明, 内置全局属性保存在这里
        ObjectEnvironmentRecord: {
            name: 'xiaoming',
            age: 18,
            listen: <function>,
            // 内置属性(包括但不限于以下两个)
            Array: <construct function>,
            Object: <construct function>
        }
    }
}

// listen 函数词法环境
listenFunctionEnviroment = {
    // 全局环境下定义外部引用为全局环境
    outer: GlobalEnvironment,
    // 函数环境记录
    FunctionEnvironmentRecord: {
        // binding object, 全局调用 this 指向全局环境
        [[This]]: GlobalEnvironment
        BindingObject: {
            text: 'hi'
        }
    }
}

我们现在知道全局词法环境的 GlobalObject 其实就是 window ,但是看下面例子

var name = 'xiaoming';
let name = 'xiaodong'; // SyntaxError

window.age = 20;
let age = 18;

可以看到在设置 name 属性的时候报错了,但是设置 age 的时候没有,是因为修改 GlobalObject(window)时,为了区分是声明的方式还是显示修改 GlobalObject,全局环境记录会通过 [[VarName]] 来区分,name 会被添加到 [[VarName]]age 不会

并且我们还能发现当我们打印 age 的时候会打印出 18 而不是 20 是因为 全局环境记录 在检索是会优先检索 声明式环境记录 再检索 对象环境记录

外部环境引用(outer)

词法环境对其外部词法环境的引用,且外部词法环境又拥有对其自身的外部词法环境的引用所组成的链状结构,称为 环境链作用域链

环境链的存在就是为了标识符的查找解析,假如自身找不到,那么就去外部词法环境中寻找,看下面例子

function person() {
    var name = 'xiaoming';
    
    function people() {
        console.log(name, age);
    }
    
    return people;
}

var people = person();
var name = 'xiaodong';
var age = 18;
people(); // xiaoming 18

从上面的例子还表明了 环境链 在定义时就已经确定了


重新学习,如发现错漏地方,欢迎更正,共勉