1。编译原理 

 

尽管通常将 JavaScript 归类为“动态”或“解释执行”语言,但事实上它是一门编译语言。

 

在传统编译语言的流程中,程序中的一段源代码在执行之前会经历三个步骤,统称为“编译”。 

 

分词/词法分析(Tokenizing/Lexing) 

将由字符组成的字符串分解成(对编程语言来说)有意义的代码块,这些代 码块被称为词法单元(token)。 

分词(tokenizing)和词法分析(Lexing)之间 主要差异在于词法单元的识别是通过有状态还是无状态的方式进行的。

如果词法单元生成器在判断 a 是一个独立的词法单元还是其他词法 单元的一部分时,调用的是有状态的解析规则,那么这个过程就被称为词法 分析。 

 

解析/语法分析(Parsing)

这个过程是将词法单元流(数组)转换成一个由元素逐级嵌套所组成的代表了程序语法 结构的树。这个树被称为“抽象语法树”(Abstract Syntax Tree,AST)。

 

代码生成
将 AST 转换为可执行代码的过程称被称为代码生成。

 

JavaScript 的工作原理 

引擎
从头到尾负责整个 JavaScript 程序的编译及执行过程。 

编译器

负责语法分析及代码生成等 

作用域

负责收集并维护由所有声明的标识符(变量)组成的一系列查 询,并实施一套非常严格的规则,确定当前执行的代码对这些标识符的访问权限。 

 

例如:var a=2;

变量的赋值操作会执行两个动作,首先编译器会在当前作用域中声明一个变量(如 果之前没有声明过),然后在运行时引擎会在作用域中查找该变量,如果能够找到就会对 它赋值。 

 

补充:

引擎在查找变量 a 的过程由作用域进行协助 ,引擎会为变量 a 进行 LHS 查询。另外一个查找的类型叫作 RHS。

L”和“R”的含义,分别代表左侧和右侧。 即一个赋值操作的左侧和右侧。 

准确的说,RHS 查询与简单地查找某个变量的值别无二致,而 LHS 查询则是试图找到变量的容器本身,从而可以对其赋值。从这个角度说,RHS 并不是真正意义上的“赋值操作的右侧”,

更准确地说是“非左侧”。你可以将 RHS 理解成 retrieve his source value(取到它的源值),这意味着“得到某某的 值”。 

因此在概念上最 好将其理解为“赋值操作的目标是谁(LHS)”以及“谁是赋值操作的源头(RHS)”。 

 

什么是作用域?

作用域是根据名称查找变量的一套规则。 

 

什么是作用域嵌套 ?

当一个块或函数嵌套在另一个块或函数中时,就发生了作用域的嵌套。因此,在当前作用域中无法找到某个变量时,引擎就会在外层嵌套的作用域中继续查找,直到找到该变量,

或抵达最外层的作用域(也就是全局作用域)为止。 

 

遍历嵌套作用域链的规则:引擎从当前的执行作用域开始查找变量,如果找不到, 就向上一级继续查找。当抵达最外层的全局作用域时,无论找到还是没找到,查找过程都 会停止。 

 

小结 

作用域是一套规则,用于确定在何处以及如何查找变量(标识符)。如果查找的目的是对变量进行赋值,那么就会使用 LHS 查询;如果目的是获取变量的值,就会使用 RHS 查询。

赋值操作符会导致 LHS 查询。=操作符或调用函数时传入参数的操作都会导致关联作用域 的赋值操作。 

LHS 和 RHS 查询都会在当前执行作用域中开始,如果有需要(也就是说它们没有找到所 需的标识符),就会向上级作用域继续查找目标标识符,这样每次上升一级作用域(一层 楼),最后抵达全局作用域(顶层),无论找到或没找到都将停止。 

不成功的 RHS 引用会导致抛出 ReferenceError 异常。不成功的 LHS 引用会导致自动隐式 地创建一个全局变量(非严格模式下),该变量使用 LHS 引用的目标作为标识符,或者抛 出 ReferenceError 异常(严格模式下)。