你不知道的JS(一) - 作用域是什么

223 阅读2分钟

1. 编译原理

JS 与其他强类型语言不同,不是提前编译的,编译结果也不能在分布式系统中进行移植。传统编译语言在执行之前都会经历以下三个步骤:

  1. 分词/词法分析

在这里会将字符组成的字符串分解成有意义的代码块,这个代码块被成为词法单元。如 var a = 2;被分解成 var、 a、 =、 2。空格会不会被当作词法单元取决于空格在这门语言中是否具有意义。

  1. 解析/语法分析

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

  1. 代码生成

将 AST 转换成为可执行的代码的过程。例如:内部有某种方法将 var a = 2 的 AST 树转换成一组机器指令,用来创建一个叫做 a 的变量(包括分配内存等),并将一个值的存在 a 中。

JS 代码片段在执行前都要进行编译。

2. 理解作用域

· 引擎

从头到位负责 JS 的程序的编译及执行过程。

· 编译器

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

· 作用域

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

一个程序解释它们之间的合作:

function foo(a) {
	console.log(a); // 2
}
foo(2);

引擎会先对 foo(2) 在作用域内进行 RHS(右查询),执行的时候遇到形参 a 会在作用域内进行 LHS(左查询),然后把 2 赋值给 a 。然后对 console 进行 RHS 引用,然后找到 log() 函数;然后再次对 a 的 RHS 进行确认。最后打印出 2。

注:RHS 查询在在所有嵌套的作用域中找不到所需的变量时,引擎会抛出 ReferenceError 异常,当 RHS 查询到一个变量,但是若进行不合理的操作,比如试图对一个非函数类型的值进行调用,或者引用 null 或 undefined 类型的值中的属性,那引擎会抛出另外一种类型的异常,叫做 TypeError。相比较 LHS 查询时,如果在顶层(全局作用域)中也找不到目标变量时,全局作用域中会创建一个具有该名称的变量,并返回给引擎,前提时在非严格模式下。在严格模式下不会创建全局变量,会抛出类似 ReferenceError 异常。