讀古今文學網 > 編寫高質量代碼:改善JavaScript程序的188個建議 > 建議153:警惕人為改變作用域鏈 >

建議153:警惕人為改變作用域鏈

一般來說,一個運行期上下文的作用域鏈不會被改變,但是,有兩種表達式可以在運行時臨時改變運行期上下文作用域鏈。

(1)with

with表達式為所有對象屬性創建一個默認操作變量。在其他語言中,類似的功能通常用來避免書寫一些重複的代碼。initUI函數可以重寫成如下形式:


function initUI{

with(document){

var bd=body,links=getElementsByTagName_r("a"),i=0,len=links.length;

while(i<len){

update(links[i++]);

}

getElementById("go-btn").onclick=function{

start;

};

bd.className="active";

}

}


在上面代碼中,重寫的initUI函數使用了一個with表達式,避免了多次書寫document。這樣似乎更有效率,實際上卻產生了一個性能問題。

當代碼流執行到一個with表達式時,運行期上下文的作用域鏈被臨時改變了。一個新的可變對像將被創建,它包含指定對象的所有屬性。此對像被插入到作用域鏈的前端,這意味著現在函數的所有局部變量都被推入第二個作用域鏈對像中,所以訪問代價更高了。

通過將document對像傳遞給with表達式,一個新的可變對像容納了document對象的所有屬性,被插入到作用域鏈的前端。這使得訪問document對象的速度非常快,但訪問局部變量的速度卻變慢了,如bd變量。正因為如此,最好不要使用with表達式,只要簡單地將document存儲在一個局部變量中,就可以獲得性能上的提升。

(2)catch

在JavaScript中,不只是with表達式人為地改變運行期上下文的作用域鏈,try catch表達式的catch子句也具有相同效果。當try塊發生錯誤時,程序流程自動轉入catch塊,並將異常對像推入作用域鏈前端的一個可變對像中。在catch塊中,函數的所有局部變量現在被放在第二個作用域鏈對像中,例如:


try{

methodThatMightCauseAnError;

}catch(ex){

alert(ex.message);

}


注意,只要catch子句執行完畢,作用域鏈就會返回到原來的狀態。如果使用得當,那麼try catch表達式將是非常有用的語句,所以不建議完全避免使用try catch語句。如果計劃使用一個try catch語句,那麼一定要確保瞭解可能發生的錯誤。一個try catch語句不應該作為解決JavaScript錯誤的辦法。如果一個錯誤會經常發生,那說明應當修正代碼本身的問題。

可以通過精減代碼的辦法將catch子句對性能的影響降至最低。一個好的模式是將錯誤交給一個專用函數來處理,例如:


try{

methodThatMightCauseAnError;

}catch(ex){

handleError(ex);

}


handleError函數是catch子句中運行的唯一代碼。此函數以適當方法自由地處理錯誤,並接收由錯誤產生的異常對象。由於只有一條語句,沒有局部變量訪問,作用域鏈臨時改變就不會影響代碼的性能。