1 词法化
大部分标准表一起的第一个工作阶段是词法化,对源代码的字符进行检查,如果是有状态的解析过程还会赋予单词语义;
一般来说词法作用域是由书写代码时,把变量和块作用域写在哪里决定的,因此词法分析器在处理代码时会保持作用域不变(大部分情况是这样,而且我们推荐这样写);
2 遮蔽效应
作用域查找时,会在找到第一个匹配的标识符时停止,这叫做遮蔽效应;
全局变量会自动的成为全局对象(比如浏览器中的window对象)的属性,,因此不直接使用词法名称而是通过属性,比如window.a,就可以访问到被遮蔽的属性,但是如果是非全局变量被遮蔽了是无法访问的;
无论函数在哪里被调用以及如何调用,它的词法作用域都是由声明的位置决定;
3 欺骗词法
3.1 eval
JavaScript中的eval()函数可以接受一个字符串作为参数,其中的内容视为书写时就存在于此的代码;因此执行到eval()时,这段代码会动态插进来,并对词法作用域的环境进行修改;
严格模式下,eval()运行时有自己的词法作用域,也就无法修改所在的作用域;
3.2 with
with通常作为重复引用一个对象的多个属性的快捷方式,比如:
var obj={
a:1,
b:2
}
with(obj){
a=3;
b=5;//可以像使用词法变量一样使用
}
事实上with会把一个对象处理为一个完全隔离的词法作用域,因此如果写了
with(obj){ a=3; b=5; c=6}//会导致声明一个全局变量c,赋值为6,因为这里是LHS查找
严格模式下不允许使用with;
3.3性能
欺骗词法最大的问题在于会让js引擎失去它在编译阶段进行的性能优化,部分优化基于能够依赖词法的静态分析,预先确定所有变量和函数的定义位置,才能在执行时快速找到;但是使用了eval或with,js引擎只能假设关于标识符位置的判断都是无效的,因为不知道eval会接受什么代码,也不知道with创建的新作用域的内容使什么;