词法作用域 --《你不知道的JS》
1.什么是词法作用域?
词法作用域就是定义在词法阶段的作用域。换句话说,词法作用域是由你在写代码时将变量和块作用域写在哪里来决定的,因此当词法分析器处理代码时会保持作用域不变 。
2.欺骗词法
怎么在才能在运行中 “修改”词法作用域呢??
答案:JavaScript中有两种机制实现这个目的,不过值得注意的是欺骗词法作用域会导致性能下降
两种机制如下
1.eval() 函数 ---魔鬼
函数可以接受一个字符串为参数,这个字符相当于可以运行的JavaScript代码,在执行eval之后,引擎并不知道eval是以动态的方式进入的,并对词法环境进行修改。
function foo(str, a) { eval( str ); // 欺骗! console.log( a, b ); } var b = 2; foo( "var b = 3;", 1 ); // 1, 3
在执行eval之后,var b = 2; 会生成一个词法环境,即在foo作用域里面定义了b变量,console.log访问时首先会在自己的词法作用域里寻找a,b变量。类似的有setTimeout(),setInterval();
3.with()
with通常被当作重复引用同一个对象中的多个属性的快捷方式,可以不需要重复引用对象本身。
var obj = { a: 1, b: 2, c: 3 }; // 单调乏味的重复"obj" obj.a = 2; obj.b = 3; obj.c = 4; // 简单的快捷方式 with (obj) { a = 3; b = 4; c = 5; }
3.性能
JavaScript引擎会在编译阶段进行数项的性能优化。其中有些优化依赖于能够根据代码的词行静态分析,并预先确定所有变量和函数的定义位置,才能在执行过程中快速找到标识符 。如果引擎在代码中发现了eval(..)或with.
它只能简单地假设关于标识符位置的判断都是无效的,因为无法在词法分析阶段明确知道eval(..)会接收到什么代码,这些代码会如何对作用域进行修改,也无法知道传递给with用来创建新词法作用域的对象的内容到底是什么。