作用域是什么
1. 编译原理
开发者大多把JavaScript归为“动态”或者“解释执行”语言,但是实际上JavaScript也是编译语言,只是并不是提前编译的。JavaScript的编译发生在代码执行前的几微秒。
代码的编译分为以下步骤:
1. 分词/词法分析 以JavaScript为例, var a = 2; 这段程序会被分解成以下词法单元: var、a、=、2、;。空格是否会当做词法单元取决于空格在这门语言中是否有意义
2. 解析/语法分析 将分词得到的词法单元转换成“抽象语法树”(AST)
3. 代码生成 将AST转换为可执行代码的过程 然而,比起上述的编译过程只有三个步骤的语言的编译器,JavaScript的引擎要复杂得多。JavaScript引擎在语法分析和代码生成阶段会对运行性能进行优化,包括对冗余元素的优化。
2.理解作用域
在作用域中参与工作的有以下几个部分: 引擎、编译器、作用域。 当执行var a = 2;时 编译器先将代码分解成词法单元,然后解析为“抽象语法树”。当开始进行代码生成时,对这段代码的处理方式会和预期的有所不同。
编译器的处理如下:
1. 遇到var a,会先去询问该作用域是否已经存在a变量。若存在,则忽略var 声明;否则要求作用域在该作用域生成一个变量a。
2. 接下来编译器生成引擎运行时所需的代码,这些代码呗用来处理a = 2这个赋值操作。引擎运行时先询问作用域当前作用域是否存在一个叫a的变量,若是,则使用这个变量,否则继续查找该变量直到找到,或者到全局作用域(不管找没找到)而停止。
总结:变量赋值的的过程概括来说就是 1. 编译器声明变量。2. 引擎查找变量,找到赋值。
3.LHS和RHS
区分LHS和RHS是一件非常重要的事情!!!!
1. LHS:查找变量赋值操作的目标
2. RHS:取源值,非LHS即不是赋值操作的目标都为RHS
以下为例子:
引擎和作用域: 引擎想作用域去讨要引用,引擎执行代码操作
4.作用域嵌套
在当前作用域无法找到某个变量时,引擎会在外层作用域继续查找,知道找到该变量,或者到全局作用域(不管找没找到)而停止。
5.异常
现在我们来讲讲为什么区分LHS和RHS是一件重要的事情。
在变量还没有声明(在任何作用域中都无法找到该变量)的情况下,这两种查询行为不同。
下列代码的真实操作为右边的代码块
var b
function foo(a) { function foo(a) {
console.log(a + b) ==> console.log(a + b) // 这里对b是RHS查询的,因为无法找到,所以抛出ReferenceError错误
b = a b = a //这里对b进行LHS查询,直到全局作用域都没有查到,又因不是strict模式,则在全局作用域自动创建了b
} }
foo(2)复制代码
RHS查询
: 在所有嵌套的作用域中都无法找到时,会抛出ReferenceError
LHS查询
:无法找到时分为两种情况
1. 非strict模式下: 会热心的创建一个变量,当然是在全局作用域中创建。
2. 在strict模式下: 同样会抛出ReferenceError
另外RHS找到了这个变量,
但是你对这个变量进行不合理的操作,如:非函数类型值的函数调用、引用null/undefined类型的值的属性。则会抛出TypeError。 ReferenceError错误和作用域判别失败相关,而TypeError则是作用域判别成功,但是对结果操作是非法或不合理的。
注: LSH和RSH都是从当前作用域开始向顶层查找的。