第一章 作用域是什么
1.1 编译原理
- 分词/词法分析:将代码分成对编译器有意义的词法单元
- 解析/语法分析:将词法单元转换为抽象语法树
- 代码生成:生成可执行的计算机代码(计算机内部部分操作)
1.2 理解作用域
- 遇到
var a
,编译器会询问作用域是否已经有一个该名称的变量存在于同一个作用域的集合中。如果是则会忽略声明,继续进行编译;否则会要求作用域在当前作用域的集合中声明一个新的变量并命名为a
- 接下来编译器会为引擎生成运行时所需的代码,这些代码被用来处理
a = 2
这个赋值操作,引擎运行时会首先询问作用域,在当前作用域集合中是否存在一个叫做a
的变量。如果是,引擎就会使用这个变量;否则引擎会继续查找该变量
LHS:对变量进行赋值
RHS:对变量进行取值
function foo(a) {
var b = a;
return a + b;
}
var c = foo(2);
3处LHS4处RHS
LHS:c = ..., b = ..., foo(2)隐含a = 2
RHS:... = foo(2), ... = a, return a + b两次
1.3作用域嵌套
- 引擎从当前的执行作用域开始查找变量,如果找不到就像上一级继续查找。当抵达最外层全局作用域时,无论是否找到,都会停止查找
1.4 异常
- 当在所有的作用域中对某个变量进行RHS查询都无法找到时,引擎就会抛出ReferenceError
- 在非严格模式下,当在所有作用域中对每个变量进行LHS查询都无法找到时,引擎会在全局作用域中创建一个具有该名称的变量;在严格模式下则会使用LHS引用的目标作为标识符,或者抛出ReferenceError
第二章 词法作用域
2.1 词法阶段
- 词法作用域完全由写代码时函数声明的位置定义
查找
- 作用域查找会在找到第一个匹配的标识符时停止,因此产生了变量的“遮蔽效应”
- 全局变量会自动成为全局对象(如浏览器中的window对象)的属性,因此可以通过全局对象属性来间接访问那些被遮蔽的全局变量:
window.全局变量
2.2 欺骗词法
JavaScript中有两种机制来实现运行时“修改”词法作用域,但值得注意的是:欺骗词法作用域会导致性能下降
2.2.1 eval
function foo(str, a) {
eval(str);// 欺骗
console.log(a,b);
}
var b=2;
foo("var b=2;",1);// 1,3
eval
的作用就是将参数中的代码加入到函数中,就好像是参数中的代码就在编写的时候就写在foo
函数当中