是不是有时候我太理性了?
我的同事说这个猫的眼神像我。我要是如我猫这般可爱多好啊~
1. 词法阶段
大部分标准语言编译器的第一个工作阶段叫做词法化。词法化的过程会对源代码中的字符进行检查,如果是有状态的解析过程,还会赋予单词语义。
词法作用域就是定义在词法阶段的作用域。词法作用域是由你在写代码时将变量和块作用域下载哪里来决定的。
2. 欺骗词法(修改词法作用域)
欺骗词法作用域会导致性能下降。
两种欺骗词法作用域的机制:
2.1 eval
eval()函数接受一个字符串为参数,并将其中的内容视为在书写时就存在于程序中这个位置的代码。
function foo(str, a) {
eval( str ); // 欺骗!
console.log( a, b );
}
var b = 2;
foo( "var b = 3;", 1 ); // 1, 3
这段代码在foo()内部创建了一个变量b,遮蔽了外部的b。
在程序中动态生成代码的使用场景非常罕见,因为它带来的好处无法抵消性能上的损失。
2.2 with
eval通常被当做重复引用同一个对象中的多个属性的快捷方式,可以不需要重复引用对象本身。
比如:
var obj = {
a : 1,
b : 2,
c : 3
}
// 修改obj的值
obj.a = 2;
obj.b = 3;
obj.a = 4;
// 快捷方式
with(obj){
a = 1;
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 被泄漏到全局作用域上了!
当我们传递 o1 给 with 时,with 所声明的作用域是 o1,而这个作用域中含 有一个同 o1.a 属性相符的标识符。但当我们将 o2 作为作用域时,其中并没有 a 标识符, 因此进行了正常的 LHS 标识符查找
有个副作用就是a=2赋值操作创建了一个全局的变量a。
尽管 with 块可以将一个对象处理为词法作用域,但是这个块内部正常的 var声明并不会被限制在这个块的作用域中,而是被添加到 with 所处的函数作 用域中。
3. 性能
JS引擎会在编译阶段进行数项的性能优化。其中有些依赖于能够根据代码的词法进行静态分析,并预先确定所有变量和函数的定义位置,才能在执行过程中快速找到标识符。
最悲观的情况是,如果出现eval()和with,所有的优化可能都是无意义的,因此最简单的做法就是不做任何优化。
so,不要使用他们!!!
愿我们有能力不向生活缴械投降---Lin