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語言可能會更好。