LHS查询:对变量进行赋值。
RHS查询:获取变量的值,查找。
作用域:确定在何处以及如何查找变量(标识符)。
在严格模式中,LHS 查询失败时,并不会创建并返回一个全局变量,引擎会抛出同RHS 查询失败时同样的ReferrenceError 异常。
RHS 查询找到了一个变量,但是你尝试对这个变量的值进行不合理的操作,比如对一个非函数类型的值进行函数操作,或者引用null 或undefined 类型的值中的属性,那么引擎会抛出另一类型的TypeError 类型的异常。
ReferenceError 与作用域判别失败相关,而TypeError 则代表作用域判别成功了,但对结果的操作是非法的不合理的。
不成功的 RHS 引用会导致抛出 ReferenceError 异常。不成功的 LHS 引用会导致自动隐式 地创建一个全局变量(非严格模式下),该变量使用 LHS 引用的目标作为标识符,或者抛 出 ReferenceError 异常(严格模式下)。
词法作用域
默认情况下,如果eval(..) 中所执行的代码包含有一个或多个声明(无论是变量还是函数),就会对eval(..) 所处的词法作用域进行修改。
在严格模式的程序中,eval(..) 在运行时有其自己的词法作用域,意味着其中的声明无法修改所在的作用域。
JavaScript 中还有其他一些功能效果和eval(..) 很相似。setTimeout(..) 和setInterval(..) 的第一个参数可以是字符串,字符串的内容可以被解释为一段动态生成的函数代码。这种动态生成的代码带来的好处无法抵消性能上的损失。
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 块可以将一个对象处理为词法作用域,但是这个块内部正常的var声明并不会被限制在这个块的作用域中,而是被 添加到with 所处的函数作用域中。
词法作用域意味着作用域是由书写代码时函数声明的位置来决定的。编译的词法分析阶段基本能够知道全部标识符在哪里以 及是如何声明的,从而能够预测在执行过程中如何对它们进行查找。
JavaScript 中有两个机制可以“欺骗”词法作用域:eval(..) 和with。前者可以对一段包含一个或多个声明的“代码”字 符串进行演算,并借此来修改已经存在的词法作用域(在运行时)。后者本质上是通过将一个对象的引用当作作用域来处理 ,将对象的属性当作作用域中的标识符来处理,从而创建了一个新的词法作用域(同样是在运行时)。
这两个机制的副作用是引擎无法在编译时对作用域查找进行优化,因为引擎只能谨慎地认为这样的优化是无效的。使用这其 中任何一个机制都将导致代码运行变慢。不要使用它们。(JavaScript 引擎会在编译阶段进行数项的性能优化。其中有些优 化依赖于能够根据代码的词法进行静态分析,并预先确定所有变量和函数的定义位置,才能在执行过程中快速找到标识符。 )