【你不知道的JavasSript(上)读书笔记】第1章:作用域
该系列是《你不知道的JavaScript(上)》的读书笔记。记录成文字,加深学习印象。
一、JavaScript编译原理
传统的语言编译一般经历三个过程:
- 分词/词法分析
- 解析/语法分析
- 代码生成
而JavaScript引擎则要复杂得多了。简单来说就是任何JavaScript代码在执行前都要进行编译(通常在代码执行前)。二、理解作用域
要理解作用域之前需要了解下什么是作用域,它有什么作用?同时还需要了解JavaScript引擎和编译器是什么? - 引擎
从头到尾负责整个JavaScript程序的编译及执行过程。 - 编译器
负责语法分析及代码生成 - 作用域
负责收集并维护由所有声明的标识符(及变量)组成的一系列查询,并实施一套规则,确定当前执行的代码对这些标识符的访问权限(简而言之就是规定了谁有权限访问哪些变量)。
现在以var a = 2
这个简单的变量命名过程来分析,JavaScript引擎会将其看做两步var a;
和a = 2
两步进行。详见下面的流程图
总结:变量的赋值操作会执行两个动作,首先编译器会在当前作用域声明一个变量(如果之前没有声明过),然后在运行时引擎会在作用域查找该变量,如果能够找到便对它进行赋值。关于变量查找的类型
编译器在编译过程,会对变量进行查询。一种是LHS查询,另外一种查找的类型是RHS。 - LHS 当变量出现在赋值操作的左侧时进行LHS查询 (赋值操作的目标是谁)
- RHS 当变量出现在赋值操作的非左侧时,进行RHS查询(谁是赋值操作的源头,取到这个变量的源值)
试着找出下面的例子各有多少个LHS和RHS1
2
3
4
5function foo(a){
var b = a;
return b+a;
}
var c = foo(2);
答案在结尾
三、作用域嵌套
所谓作用域嵌套就是当一个块或函数嵌套在另外一个块或函数中,就发生了作用域的嵌套。因此在当前作用域无法找到该变量时,就会往外层嵌套作用域中继续寻找,直到找到该变量或者抵达最外层的作用域(也就是全局作用域)为止。
四、异常
前面提到的LHS和RHS两种查询,如何区分它们是非常重要的一件事。
因为在变量尚未声明之前,二者的查询行为是不一样的。如下面例子1
2
3
4
5function foo(a){
console.log(b+a);
b = a;
}
foo(2);
想一下,输出的值应该是什么?
答案:Uncaught ReferenceError: b is not defined(…)
,结果会报错。因为b
并没有被定义,因此引擎就抛出ReferenceError
异常。
为什么会导致这样的结果呢?这是因为在对变量b
进行RHS查询的时候,如果在作用域中没有找到该变量,也就是说明这是一个“未声明”的变量,这时候引擎就会抛出ReferenceError
异常。
相比较之下,如果是对变量b
进行LHS查询的时候,如果在全局作用域也没有找到该变量的话,全局作用域便会自动创建该变量,前提是在非严格模式下。这就是LHS和RHS的两种查询类型的区别。
同样,在JavaScript中,也有两种异常类型。一种就是刚刚说到的ReferenceError
,另外一种则是TypeError
。那这二者有什么区别吗?
ReferenceError
指的是同作用域判别失败相关,简单说就是在作用域找不到该变量。TypeError
指的是作用域判别成功,但对结果的操作是非法或者不合理的。简单说就是在作用域找到该变量,但是该变量的值不符合。
前面问题的答案
LHS
c=
a=2
b=
。
RHSfoo(2)
=a
a
(return的时候要去查找a的值)b
(return的时候要去查找b的值)