複雜算法通常比較容易使用遞歸實現。很多傳統算法正是通過遞歸實現的,如階乘函數。
function factorial(n){
if(n==0){
return 1;
}else{
return n*factorial(n-1);
}
}
遞歸函數的問題:錯誤定義或缺少終結條件會導致函數長時間運行,使瀏覽器出現假死現象。此外,遞歸函數還會受到瀏覽器調用棧大小的限制。
JavaScript引擎所支持的遞歸數量與JavaScript調用棧大小直接相關。只有IE例外,因為它的調用棧與可用系統內存相關,而其他瀏覽器有固定的調用棧限制。當使用了太多的遞歸,超過最大調用棧尺寸時,瀏覽器會彈出錯誤信息。
調用棧溢出錯誤可以用try catch語句捕獲。異常類型因瀏覽器而不同:在Firefox中,它是一個InternalError錯誤;在Safari和Chrome中,它是一個RangeError錯誤;在IE中會拋出一個一般性的Error類型;在Opera中不拋出錯誤,但會終止JavaScript引擎。正確處理這些錯誤的方法如下:
try{
recurse;
}catch(ex){
alert("errorInfo");
}
當出現調用棧尺寸限制的問題時,第一步應該定位在代碼中的遞歸實例上。為此,有兩個遞歸模式可供選擇。
第一種是直接遞歸模式,即一個函數調用自身。當發生錯誤時,這種模式比較容易定位。其一般模式如下:
function recurse{
recurse;
}
recurse;
第二種是精巧模式,它包含兩個函數:
function first{
second;
}
function second{
first;
}
first;
在這種遞歸模式中,兩個函數互相調用對方,形成一個無限循環。大多數調用棧錯誤與這兩種模式有關。常見的棧溢出原因是一個不正確的終止條件,因此定位模式錯誤的第一步是驗證終止條件。如果終止條件是正確的,那麼算法包含了太多層遞歸,為了能夠安全地在瀏覽器中運行,應改用迭代、製表或混合模式。