欺骗词法
JavaScript 中有两种机制能够实现在运行程序时 "修改" (也可以说是欺骗) 词法作用域.
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(str) 调用的其实是 var b = 3. 这段代码会当做本身就在此处一样被处理. 因此引擎会在当前作用域中就找到 b 即 3.
with
JavaScript 另外一个难以掌握的 (并且现在不推荐使用的) 用来欺骗词法作用域的功能.
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; }
但实际上这不仅仅是为了方便访问对象属性. 考虑如下代码:
function foo(obj) { with(obj){ a = 2; } } var o1 = { a:3 } var o2 = { b:3 } foo(o1); console.log(o1.a); // 2 foo(o2); console.log(o2.a); //undefined console.log(a); // 2 a被泄露到全局作用域了
with 可以将一个没有或者有多个属性的对象处理为一个完全隔离的词法作用域, 因此这个对象的属性也会被处理为定义在这个作用域中的词法标识符
关于性能, JavaScript 引擎会在编译阶段进行数项的性能优化. 其中有些优化依赖于能够根据代码的此法进行静态分析, 并预先确定所有变量和函数的定义位置,才能在执行过程中快速找到标识符.但如果引擎在代码中发现了 eval 和 with, 他只能简单的假设关于标识符位置的判断都是无效的, 因为无法再词法分析阶段明确知道 eval 会接受到什么代码, 这些代码会如何对作用域进行修改, 也无法知道传递给 with 用来创建新词法作用域的对象的内容到底是什么.
如果出现了 eval 或者 with, 所有的优化可能都是无意义的, 因此最简单的做法就是完全不做任何优化.
建议不要使用这些, 仅做知识扩充...