浅析JavaScript词法环境

468 阅读4分钟

词法环境

词法环境是ECMA中的一个规范类型 —— 基于代码词法嵌套结构用来记录标识符和具体变量或函数的关联。 简单来说,词法环境就是建立了标识符——变量的映射表。这里的标识符指的是变量名称或函数名,而变量则是实际变量原始值或者对象/函数的引用地址。

词法环境的类型

全局环境(global environment)

全局环境是一个没有外部环境的词法环境,它的外部环境引用为 null。全局环境的环境记录可以绑定变量,并关联对应的全局对象。

模块环境(module environment)

模块环境也是一个词法环境,它包含模块顶级声明的绑定,还包含模块显式导入的绑定。模块环境的外部环境是 全局环境。

函数环境(function environment)

函数环境也是一个对应于ECMAScript函数对象调用的词法环境。函数环境可以建立新的此绑定。函数环境还支持super调用所需的状态。

词法环境的组成

环境记录、外部引用和this绑定

环境记录

代码中声明的变量和函数都会存放在EnvironmentRecord中等待执行时访问。

声明环境记录(Declarative Environment Records)

声明环境记录绑定变量声明,其中包含:var、let、const、class、module、 import、function

函数环境记录(Function Environment Records)

函数环境记录为每个函数作用域以及下文环境的具体情况

模块环境记录(Module Environment Records)

模块环境记录用于表示ECMAScript模块的外部范围以及绑定情况

对象环境记录(Object Environment Records)

记录每个对象增删改查。

全局环境记录(Global Environment Records)

全局环境记录用于表示所有ECMAScript Script元素共享的最外层作用域。

全局环境记录提供内置全局变量,全局对象的属性以及脚本中发生的所有顶级声明的绑定。

全局环境记录逻辑上来说是单个记录,但是实际上可以看作是对一个对象式环境记录组件和一个声明式环境记录组件的封装。

在对象环境记录中存放的是全局对象函数、function函数声明、async、generator、var关键词变量。

在声明环境记录则存放其他方式声明的变量,如let const class等。

外部引用

全局环境的外部词法环境引用为null。

一个词法环境可以作为多个此法环境的外部环境。

外部词法环境的引用将一个词法环境和其外部词法环境链接起来,外部词法环境又拥有对其自身的外部词法环境的引用。这样就形成一个链式结构,这里我们称其为环境链(即ES6之前的作用域链),全局环境是这条链的顶端。

环境链的存在是为了标识符的解析,通俗的说就是查找变量。首先在当前环境查找变量,找不到就去外部环境找,还找不到就去外部环境的外部环境找,以此类推,直到找到,或者到环境链顶端(全局环境)还未找到则抛出ReferenceError。

this绑定

变量环境

ES6前,声明变量都是通过var关键词声明的,在ES6中则提倡使用letconst来声明变量,为了兼容var的写法,于是使用变量环境来存储var声明的变量。

变量环境本质上仍是词法环境,但它只存储var声明的变量,这样在初始化变量时可以赋值为undefined。

案例分析

let a = 10;                                         
const b = 20;                                       
var sum;                                              
                                                       
function add(e, f){                                  
    var d = 40;                                 
    return d + e + f                         
}                                                      
                                                       
let utils = {                                       
    add                                            
}                                                      
sum = utils.add(a, b);

GlobalExecutionContext = {                                             
    LexicalEnvironment: {                                           
        EnvironmentRecord: {                                    
            type: 'object',                                 
            add: <function>,                                
            a: <uninitialized>,                             
            b: <uninitialized>,                             
            utils: <uninitialized>,                         
        },                                                       
        outer: null,                                            
        this: <globalObject>                                    
    },                                                               
    VariableEnvironment: {                                          
        EnvironmentRecord: {                                    
            type: 'object',                                 
            sum: undefined                                  
        },                                                       
        outer: null,                                            
        this: <globalObject>                                    
    },                                                               
}                                                                        
// 当运行到函数add时才会创建函数执行上下文                             
FunctionExecutionContext = {                                           
    LexicalEnvironment: {                                           
        EnvironmentRecord: {                                    
            type: 'declarative',                            
            arguments: {0: 10, 1: 20, length: 2},      
            [[ThisValue]]: <utils>,                         
            [[NewTarget]]: undefined,                       
            ...                                              
        },                                                       
        outer: <GlobalLexicalEnvironment>,                      
        this: <utils>                                           
    },                                                               
    VariableEnvironment: {                                          
        EnvironmentRecord: {                                    
            type: 'declarative',                            
            d: undefined                                    
        },                                                       
        outer: <GlobalLexicalEnvironment>,                      
        this: <utils>                                           
    },                                                               
}      

参考

深入JavaScript系列(一):词法环境

JS夯实之执行上下文与词法环境