讀古今文學網 > 編寫高質量代碼:改善JavaScript程序的188個建議 > 建議12:避免使用with >

建議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{

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