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{
obj.a=obj.b===undefined?b:obj.b;
}
因此,前面代码等价以下语句中的任何一条。
a=b;
a=obj.b;
obj.a=b;
obj.a=obj.b;
直接阅读代码不可能辨别出会得到这些语句中的哪一条。a和b可能随着程序运行到下一步时发生变化,甚至可能在程序运行过程中就发生变化了。如果不能通过阅读程序来了解它将会做什么,就无法确信它是否会正确地执行我们要求的事情。
with语句在JavaScript语言中存在,本身就严重影响了JavaScript处理器的速度,因为它阻止了变量名的词法作用域绑定。它的本意是好的,但如果没有它,JavaScript语言可能会更好。