1.1 编译原理
传统编译步骤
分词/词法分析(拆分成一个个词法单元)——>解析/语法分析(词法单元流转化为抽象语法树)——>代码生成(将抽象语法书(AST)转化为可执行代码(机器指令))
js引擎比上述要复杂,在各个阶段会有特定步骤来对性能进行优化,比如冗余元素。
其他很多语言的编译多发生在构建之前,而js大部分情况下的编译是发生在代码执行前的短短几微秒内,没有其他语言编译器那样有那么多的时间进行优化,所以js引擎想了各种办法来保证性能最佳(比如JIT,可以延迟编译甚至实施重编译)。
1.2 理解作用域
以var a = 2为例,执行这个语句需要引擎,编译器,作用域的配合。
编译器查询方式
以LHS和RHS为例,LHS查询是视图找到变量的容器本身,并对去赋值,而RHS是简单的查找某个变量的值。
比如console.log(a),这里对a的引用就是RHS,并没有进行任何赋值。
而a=2,这里对a的引用就是LHS,并不关心当前的值是什么,只是要为 '=2' 这个赋值操作找到一个目标。
注:eg:foo(a)中实际上含有一个隐式 'a=2' 操作,不要忽略这一个LHS查询。
注:函数声明function foo(a) {...}不能简单的理解为LHS查询和赋值操作(var foo,foo = function(a){...}),因为编译器可以在代码生成的同时处理声明和值的定义。
1.4 异常
在变量还没有声明的情况下,这两种查询的行为是不同的。RHS如果找不到该变量,引擎会抛出ReferenceError的错误,而LHS则会在全局作用域下创建一个具有该名称的变量,并将其返还给引擎(需要程序运行在非'严格模式'下,'严格模式'下,引擎会抛出同RHS类似的ReferenceError异常)。