建议12:避免使用with
with语句的语法如下:
- with ( Expression )
- Statement
with会把由Expression计算出来的对象添加到当前执行上下文的作用域链的前面,然后使用这个扩大的作用域链来执行语句Statement,最后恢复作用域链。不管其中的语句是否正常退出,作用域链都会被恢复。
由于with会把额外的对象添加到作用域链的前面,因此使用with可能会影响性能,并造成难以发现的错误。由于额外的对象在作用域链的前面,当执行到with语句,需要对标识符求值时,会先沿着该对象的prototype链查找。如果找不到,才会依次查找作用域链中原来的对象。因此,如果在with语句中频繁引用不在额外对象的 prototype 链中的变量,查找的速度会比不使用with 慢,例如:
- function A() {
- this.a = "A";
- }
- function B() {
- this.b = "B";
- }
- B.prototype = new A();
- function C() {
- this.c = "C";
- }
- C.prototype = new B();
- (function() {
- var myVar = "Hello World";
- alert(typeof a); // "undefined"
- var a = 1;
- var obj = new C();
- with(obj) {
- alert(typeof a); //"string"
- alert(myVar); //查找速度比较慢
- }
- alert(typeof a); // "number"
- })();
在上面代码中,先通过prototype方式实现了继承。在with语句中,执行alert(typeof a)时需要查找变量a,由于obj在作用域链的前面,而obj中也存在名为a的属性,因此obj中的a被找到。执行alert(myVar)需要查找变量myVal,而obj中不存在名为myVal的属性,会继续查找作用域链中后面的对象,因此使用with比不使用with的速度慢。需要注意的是,最后一条语句alert(typeof a)不在with中,因此查找到的a是之前声明的 number型的变量。
使用with语句可以快捷地访问对象的属性,然而,得到的结果有时可能是不可预料的,所以应该避免使用它。例如:
- with (obj) {
- a = b;
- }
上面代码与下面的代码完成的是同样的事情。 - if (obj.a === undefined) {
- a = obj.b === undefined ? b : obj.b;
- } else {
- objobj.a = obj.b === undefined ? b : obj.b;
- }
因此,前面代码等价以下语句中的任何一条。 - a = b;
- a = obj.b;
- obj.a = b;
- objobj.a = obj.b;
直接阅读代码不可能辨别出会得到这些语句中的哪一条。a和b可能随着程序运行到下一步时发生变化,甚至可能在程序运行过程中就发生变化了。如果不能通过阅读程序来了解它将会做什么,就无法确信它是否会正确地执行我们要求的事情。
with语句在JavaScript语言中存在,本身就严重影响了JavaScript处理器的速度,因为它阻止了变量名的词法作用域绑定。它的本意是好的,但如果没有它,JavaScript语言可能会更好。
时间: 2024-12-24 23:36:09