js錯誤處理與調試理論和辦法 (轉)

js錯誤處理與調試理論和辦法

ECMA-262 第 3 版引入了 try-catch 語句,作爲 JavaScript 中處理異常的一種標準方式。基本的語
法如下所示,顯而易見,這與 Java 中的 try-catch 語句是完全相同的。
try{
// 可能會導致錯誤的代碼
} catch(error){
// 在錯誤發生時怎麼處理
}
也就是說, 我們應該把所有可能會拋出錯誤的代碼都放在 try 語句塊中, 而把那些用於錯誤處理的
代碼放在 catch 塊中。例如:
try {
window.someNonexistentFunction();
} catch (error){
alert("An error happened!");
}
如果 try 塊中的任何代碼發生了錯誤,就會立即退出代碼執行過程,然後接着執行 catch 塊。此
時, catch 塊會接收到一個包含錯誤信息的對象。與在其他語言中不同的是,即使你不想使用這個錯誤
對象,也要給它起個名字。這個對象中包含的實際信息會因瀏覽器而異,但共同的是有一個保存着錯誤
消息的 message 屬性。 ECMA-262 還規定了一個保存錯誤類型的 name 屬性; 當前所有瀏覽器都支持這
個屬性(Opera 9 之前的版本不支持這個屬性) 。因此,在發生錯誤時,就可以像下面這樣實事求是地顯
示瀏覽器給出的消息。
try {
window.someNonexistentFunction();
} catch (error){
alert(error.message);
}
TryCatchExample01.htm
這個例子在向用戶顯示錯誤消息時,使用了錯誤對象的 message 屬性。這個 message 屬性是唯一
一個能夠保證所有瀏覽器都支持的屬性,除此之外,IE、Firefox、Safari、Chrome 以及 Opera 都爲事件
對象添加了其他相關信息。IE 添加了與 message 屬性完全相同的 description 屬性,還添加了保存
着內部錯誤數量的 number 屬性。 Firefox 添加了 fileName 、 lineNumber 和 stack (包含棧跟蹤信息)
屬性。Safari 添加了 line (表示行號) 、 sourceId (表示內部錯誤代碼)和 sourceURL 屬性。當然,
在跨瀏覽器編程時,最好還是隻使用 message 屬性。
1.  finally 子句
雖然在 try-catch 語句中是可選的,但 finally 子句一經使用,其代碼無論如何都會執行。換句
話說, try 語句塊中的代碼全部正常執行, finally 子句會執行;如果因爲出錯而執行了 catch 語句

塊, finally 子句照樣還會執行。只要代碼中包含 finally 子句,則無論 try 或 catch 語句塊中包
含什麼代碼——甚至 return 語句,都不會阻止 finally 子句的執行。來看下面這個函數。
function testFinally(){
try {
return 2;
} catch (error){
return 1;
} finally {
return 0;
}
}
TryCatchExample02.htm
這個函數在 try-catch 語句的每一部分都放了一條 return 語句。表面上看,調用這個函數會返
回 2,因爲返回 2 的 return 語句位於 try 語句塊中,而執行該語句又不會出錯。可是,由於最後還有
一個 finally 子句,結果就會導致該 return 語句被忽略;也就是說,調用這個函數只能返回 0。如果
把 finally 子句拿掉,這個函數將返回 2。
如果提供 finally 子句,則 catch 子句就成了可選的( catch 或 finally 有一個即可) 。IE7 及
更早版本中有一個 bug:除非有 catch 子句,否則 finally 中的代碼永遠不會執行。如果你仍然要考
慮 IE 的早期版本,那就只好提供一個 catch 子句,哪怕裏面什麼都不寫。IE8 修復了這個 bug。
請讀者務必要記住,只要代碼中包含 finally 子句,那麼無論 try 還是 catch 語句塊
中的 return 語句都將被忽略。因此,在使用 finally 子句之前,一定要非常清楚你想讓代
碼怎麼樣。
2. 錯誤類型
執行代碼期間可能會發生的錯誤有多種類型。每種錯誤都有對應的錯誤類型,而當錯誤發生時,就
會拋出相應類型的錯誤對象。ECMA-262 定義了下列 7 種錯誤類型:
 Error
 EvalError
 RangeError
 ReferenceError
 SyntaxError
 TypeError
 URIError

其中, Error 是基類型,其他錯誤類型都繼承自該類型。因此,所有錯誤類型共享了一組相同的屬
性(錯誤對象中的方法全是默認的對象方法) 。 Error 類型的錯誤很少見,如果有也是瀏覽器拋出的;
這個基類型的主要目的是供開發人員拋出自定義錯誤。
EvalError 類型的錯誤會在使用 eval() 函數而發生異常時被拋出。ECMA-262 中對這個錯誤有如
下描述: “如果以非直接調用的方式使用 eval 屬性的值(換句話說,沒有明確地將其名稱作爲一個
Identifier ,即用作 CallExpression 中的 MemberExpression ) ,或者爲 eval 屬性賦值。 ”簡單
地說,如果沒有把 eval() 當成函數調用,就會拋出錯誤,例如:

new eval(); //拋出 EvalError
eval = foo; //拋出 EvalError
在實踐中,瀏覽器不一定會在應該拋出錯誤時就拋出 EvalError 。例如,Firefox 4+和 IE8 對第一
種情況會拋出 TypeError ,而第二種情況會成功執行,不發生錯誤。有鑑於此,加上在實際開發中極
少會這樣使用 eval() ,所以遇到這種錯誤類型的可能性極小。
RangeError 類型的錯誤會在數值超出相應範圍時觸發。例如,在定義數組時,如果指定了數組不
支持的項數(如20 或 Number.MAX_VALUE) ,就會觸發這種錯誤。下面是具體的例子。
var items1 = new Array(-20); //拋出 RangeError
var items2 = new Array(Number.MAX_VALUE); //拋出 RangeError
JavaScript 中經常會出現這種範圍錯誤。
在找不到對象的情況下,會發生 ReferenceError (這種情況下,會直接導致人所共知的 "object
expected" 瀏覽器錯誤) 。通常,在訪問不存在的變量時,就會發生這種錯誤,例如:
var obj = x; //在 x 並未聲明的情況下拋出 ReferenceError
至於 SyntaxError ,當我們把語法錯誤的 JavaScript 字符串傳入 eval() 函數時,就會導致此類錯
誤。例如:
eval("a ++ b"); //拋出 SyntaxError
如果語法錯誤的代碼出現在 eval() 函數之外, 則不太可能使用 SyntaxError , 因爲此時的語法錯
誤會導致 JavaScript 代碼立即停止執行。
TypeError 類型在 JavaScript 中會經常用到,在變量中保存着意外的類型時,或者在訪問不存在的
方法時,都會導致這種錯誤。錯誤的原因雖然多種多樣,但歸根結底還是由於在執行特定於類型的操作
時,變量的類型並不符合要求所致。下面來看幾個例子。
var o = new 10; //拋出 TypeError
alert("name" in true); //拋出 TypeError
Function.prototype.toString.call("name"); //拋出 TypeError
最常發生類型錯誤的情況,就是傳遞給函數的參數事先未經檢查,結果傳入類型與預期類型不相符。
在使用 encodeURI() 或 decodeURI() ,而 URI 格式不正確時,就會導致 URIError 錯誤。這種
錯誤也很少見,因爲前面說的這兩個函數的容錯性非常高。
利用不同的錯誤類型,可以獲悉更多有關異常的信息,從而有助於對錯誤作出恰當的處理。

要想知道錯誤的類型,可以像下面這樣在 try-catch 語句的 catch 語句中使用 instanceof 操作符。
 try {
    someFunction();


} catch(error) {
    if (error instanceof TypeError) {
        // 處理類型錯誤
    } else if (error instanceof ReferenceError) {
        // 處理引用錯誤
    } else {
        // 處理其他類型的錯誤
    }


}

在跨瀏覽器編程中,檢查錯誤類型是確定處理方式的最簡便途徑;包含在 message 屬性中的錯誤
消息會因瀏覽器而異。
3. 合理使用 try-catch
當 try-catch 語句中發生錯誤時,瀏覽器會認爲錯誤已經被處理了,因而不會通過本章前面討論
的機制記錄或報告錯誤。對於那些不要求用戶懂技術,也不需要用戶理解錯誤的 Web 應用程序,這應
該說是個理想的結果。不過, try-catch 能夠讓我們實現自己的錯誤處理機制。
使用 try-catch 最適合處理那些我們無法控制的錯誤。假設你在使用一個大型 JavaScript 庫中的
函數,該函數可能會有意無意地拋出一些錯誤。由於我們不能修改這個庫的源代碼,所以大可將對該函
數的調用放在 try-catch 語句當中,萬一有什麼錯誤發生,也好恰當地處理它們。
在明明白白地知道自己的代碼會發生錯誤時,再使用 try-catch 語句就不太合適了。例如,如果
傳遞給函數的參數是字符串而非數值,就會造成函數出錯,那麼就應該先檢查參數的類型,然後再決定
如何去做。在這種情況下,不應用使用 try-catch 語句。
17.2.2 拋出錯誤
與 try-catch 語句相配的還有一個 throw 操作符,用於隨時拋出自定義錯誤。拋出錯誤時,必須
要給 throw 操作符指定一個值,這個值是什麼類型,沒有要求。下列代碼都是有效的。
throw 12345;
throw "Hello world!";
throw true;
throw { name: "JavaScript"};
在遇到 throw 操作符時,代碼會立即停止執行。僅當有 try-catch 語句捕獲到被拋出的值時,代
碼纔會繼續執行。
通過使用某種內置錯誤類型,可以更真實地模擬瀏覽器錯誤。每種錯誤類型的構造函數接收一個參
數,即實際的錯誤消息。下面是一個例子。
throw new Error("Something bad happened.");
這行代碼拋出了一個通用錯誤,帶有一條自定義錯誤消息。瀏覽器會像處理自己生成的錯誤一樣,
來處理這行代碼拋出的錯誤。換句話說,瀏覽器會以常規方式報告這一錯誤,並且會顯示這裏的自定義
錯誤消息。像下面使用其他錯誤類型,也可以模擬出類似的瀏覽器錯誤。
throw new SyntaxError("I don’t like your syntax.");
throw new TypeError("What type of variable do you take me for?");
throw new RangeError("Sorry, you just don’t have the range.");
throw new EvalError("That doesn’t evaluate.");
throw new URIError("Uri, is that you?");
throw new ReferenceError("You didn’t cite your references properly.");
在創建自定義錯誤消息時最常用的錯誤類型是 Error 、 RangeError 、 ReferenceError 和 TypeError 。
另外,利用原型鏈還可以通過繼承 Error 來創建自定義錯誤類型(原型鏈在第 6 章中介紹) 。此時,
需要爲新創建的錯誤類型指定 name 和 message 屬性。來看一個例子。
function CustomError(message){
this.name = "CustomError";
this.message = message;
}

CustomError.prototype = new Error();
throw new CustomError("My message");
ThrowingErrorsExample01.htm
瀏覽器對待繼承自 Error 的自定義錯誤類型,就像對待其他錯誤類型一樣。如果要捕獲自己拋出
的錯誤並且把它與瀏覽器錯誤區別對待的話,創建自定義錯誤是很有用的。
IE 只有在拋出 Error 對象的時候纔會顯示自定義錯誤消息。對於其他類型,它
都無一例外地顯示 "exception thrown and not caught" (拋出了異常,且未被
捕獲)。
1. 拋出錯誤的時機
要針對函數爲什麼會執行失敗給出更多信息,拋出自定義錯誤是一種很方便的方式。應該在出現某
種特定的已知錯誤條件,導致函數無法正常執行時拋出錯誤。換句話說,瀏覽器會在某種特定的條件下
執行函數時拋出錯誤。例如,下面的函數會在參數不是數組的情況下失敗。
function process(values){
values.sort();
for (var i=0, len=values.length; i < len; i++){
if (values[i] > 100){
return values[i];
}
}
return -1;
}
ThrowingErrorsExample02.htm
如果執行這個函數時傳給它一個字符串參數,那麼對 sort() 的調用就會失敗。對此,不同瀏覽器
會給出不同的錯誤消息,但都不是特別明確,如下所示。
  IE:屬性或方法不存在。
  Firefox: values.sort() 不是函數。
  Safari:值 undefined (表達式 values.sort 的結果)不是對象。
  Chrome:對象名沒有方法 'sort' 。
  Opera:類型不匹配(通常是在需要對象的地方使用了非對象值) 。
儘管 Firefox、Chrome 和 Safari 都明確指出了代碼中導致錯誤的部分,但錯誤消息並沒有清楚地告
訴我們到底出了什麼問題,該怎麼修復問題。在處理類似前面例子中的那個函數時,通過調試處理這些
錯誤消息沒有什麼困難。但是,在面對包含數千行 JavaScript 代碼的複雜的 Web 應用程序時,要想查找
錯誤來源就沒有那麼容易了。這種情況下,帶有適當信息的自定義錯誤能夠顯著提升代碼的可維護性。
來看下面的例子。

function process(values){
if (!(values instanceof Array)){
throw new Error("process(): Argument must be an array.");
}
values.sort();
for (var i=0, len=values.length; i < len; i++){
if (values[i] > 100){
return values[i];
}
}
return -1;
}
ThrowingErrorsExample02.htm
在重寫後的這個函數中,如果 values 參數不是數組,就會拋出一個錯誤。錯誤消息中包含了函數
的名稱,以及爲什麼會發生錯誤的明確描述。如果一個複雜的 Web 應用程序發生了這個錯誤,那麼查
找問題的根源也就容易多了。
建議讀者在開發 JavaScript 代碼的過程中,重點關注函數和可能導致函數執行失敗的因素。良好的
錯誤處理機制應該可以確保代碼中只發生你自己拋出的錯誤。
在多框架環境下使用 instanceof 來檢測數組有一些問題。詳細內容請參考
22.1.1 節。
2. 拋出錯誤與使用 try-catch
關於何時該拋出錯誤,而何時該使用 try-catch 來捕獲它們,是一個老生常談的問題。一般來說,
應用程序架構的較低層次中經常會拋出錯誤,但這個層次並不會影響當前執行的代碼,因而錯誤通常得
不到真正的處理。如果你打算編寫一個要在很多應用程序中使用的 JavaScript 庫,甚至只編寫一個可能
會在應用程序內部多個地方使用的輔助函數,我都強烈建議你在拋出錯誤時提供詳盡的信息。然後,即
可在應用程序中捕獲並適當地處理這些錯誤。
說到拋出錯誤與捕獲錯誤,我們認爲只應該捕獲那些你確切地知道該如何處理的錯誤。捕獲錯誤的
目的在於避免瀏覽器以默認方式處理它們;而拋出錯誤的目的在於提供錯誤發生具體原因的消息。
17.2.3 錯誤( error )事件
任何沒有通過 try-catch 處理的錯誤都會觸發 window 對象的 error 事件。 這個事件是 Web 瀏覽
器最早支持的事件之一,IE、Firefox 和 Chrome 爲保持向後兼容,並沒有對這個事件作任何修改(Opera
和 Safari 不支持 error 事件) 。在任何 Web 瀏覽器中, onerror 事件處理程序都不會創建 event 對象,
但它可以接收三個參數:錯誤消息、錯誤所在的 URL 和行號。多數情況下,只有錯誤消息有用,因爲
URL 只是給出了文檔的位置,而行號所指的代碼行既可能出自嵌入的 JavaScript 代碼,也可能出自外部
的文件。要指定 onerror 事件處理程序,必須使用如下所示的 DOM0 級技術,它沒有遵循“DOM2 級
事件”的標準格式。

window.onerror = function(message, url, line){
alert(message);
};
只要發生錯誤,無論是不是瀏覽器生成的,都會觸發 error 事件,並執行這個事件處理程序。然
後,瀏覽器默認的機制發揮作用,像往常一樣顯示出錯誤消息。像下面這樣在事件處理程序中返回
false ,可以阻止瀏覽器報告錯誤的默認行爲。
window.onerror = function(message, url, line){
alert(message);
return false;
};
OnErrorExample01.htm
通過返回 false ,這個函數實際上就充當了整個文檔中的 try-catch 語句,可以捕獲所有無代碼
處理的運行時錯誤。這個事件處理程序是避免瀏覽器報告錯誤的最後一道防線,理想情況下,只要可能
就不應該使用它。只要能夠適當地使用 try-catch 語句,就不會有錯誤交給瀏覽器,也就不會觸發
error 事件。
瀏覽器在使用這個事件處理錯誤時的方式有明顯不同。 在 IE 中, 即使發生 error
事件,代碼仍然會正常執行;所有變量和數據都將得到保留,因此能在 onerror 事
件處理程序中訪問它們。但在 Firefox 中,常規代碼會停止執行,事件發生之前的所
有變量和數據都將被銷燬,因此幾乎就無法判斷錯誤了。
圖像也支持 error 事件。只要圖像的 src 特性中的 URL 不能返回可以被識別的圖像格式,就會觸
發 error 事件。此時的 error 事件遵循 DOM格式,會返回一個以圖像爲目標的 event 對象。下面是
一個例子。
var image = new Image();
EventUtil.addHandler(image, "load", function(event){
alert("Image loaded!");
});
EventUtil.addHandler(image, "error", function(event){
alert("Image not loaded!");
});
image.src = "smilex.gif"; //指定不存在的文件
OnErrorExample02.htm
在這個例子中,當加載圖像失敗時就會顯示一個警告框。需要注意的是,發生 error 事件時,圖
像下載過程已經結束,也就是說不能再重新下載了。
17.2.4 處理錯誤的策略
過去,所謂 Web 應用程序的錯誤處理策略僅限於服務器端。在談到錯誤與錯誤處理時,通常要考
慮很多方面,涉及一些工具,例如記錄和監控系統。這些工具的用途在於分析錯誤模式,追查錯誤原因,
同時幫助確定錯誤會影響到多少用戶。

在 Web 應用程序的 JavaScript 這一端,錯誤處理策略也同樣重要。由於任何 JavaScript 錯誤都可能
導致網頁無法使用,因此搞清楚何時以及爲什麼發生錯誤至關重要。絕大多數 Web 應用程序的用戶都
不懂技術,遇到錯誤時很容易心煩意亂。有時候,他們可能會刷新頁面以期解決問題,而有時候則會放
棄努力。作爲開發人員,必須要知道代碼何時可能出錯,會出什麼錯,同時還要有一個跟蹤此類問題的
系統。
17.2.5 常見的錯誤類型
錯誤處理的核心,是首先要知道代碼裏會發生什麼錯誤。由於 JavaScript 是鬆散類型的,而且也不
會驗證函數的參數,因此錯誤只會在代碼運行期間出現。一般來說,需要關注三種錯誤:
  類型轉換錯誤
  數據類型錯誤
  通信錯誤
以上錯誤分別會在特定的模式下或者沒有對值進行足夠的檢查的情況下發生。
1. 類型轉換錯誤
類型轉換錯誤發生在使用某個操作符,或者使用其他可能會自動轉換值的數據類型的語言結構時。
在使用相等(==)和不相等(!=)操作符,或者在 if 、 for 及 while 等流控制語句中使用非布爾值時,
最常發生類型轉換錯誤。
第 3 章討論的相等和不相等操作符在執行比較之前會先轉換不同類型的值。由於在非動態語言中,
開發人員都使用相同的符號執行直觀的比較, 因此在 JavaScript 中往往也會以相同方式錯誤地使用它們。
多數情況下,我們建議使用全等(===)和不全等(!==)操作符,以避免類型轉換。來看一個例子。
alert(5 == "5"); //true
alert(5 === "5"); //false
alert(1 == true); //true
alert(1 === true); //false
這裏使用了相等和全等操作符比較了數值 5 和字符串 "5" 。相等操作符首先會將數值 5 轉換成字符
串 "5" ,然後再將其與另一個字符串 "5" 進行比較,結果是 true 。全等操作符知道要比較的是兩種不同
的數據類型,因而直接返回 false 。對於 1 和 true 也是如此:相等操作符認爲它們相等,而全等操作
符認爲它們不相等。使用全等和非全等操作符,可以避免發生因爲使用相等和不相等操作符引發的類型
轉換錯誤,因此我們強烈推薦使用。
容易發生類型轉換錯誤的另一個地方, 就是流控制語句。 像 if 之類的語句在確定下一步操作之前,
會自動把任何值轉換成布爾值。尤其是 if 語句,如果使用不當,最容易出錯。來看下面的例子。
function concat(str1, str2, str3){
var result = str1 + str2;
if (str3){ //絕對不要這樣!!!
result += str3;
}
return result;
}
這個函數的用意是拼接兩或三個字符串,然後返回結果。其中,第三個字符串是可選的,因此必須
要檢查。第 3 章曾經介紹過,未使用過的命名變量會自動被賦予 undefined 值。而 undefined 值可以
被轉換成布爾值 false ,因此這個函數中的 if 語句實際上只適用於提供了第三個參數的情況。問題在

於,並不是只有 undefined 纔會被轉換成 false ,也不是隻有字符串值纔可以轉換爲 true 。例如,
假設第三個參數是數值 0 ,那麼 if 語句的測試就會失敗,而對數值 1 的測試則會通過。
在流控制語句中使用非布爾值,是極爲常見的一個錯誤來源。爲避免此類錯誤,就要做到在條件比
較時切實傳入布爾值。實際上,執行某種形式的比較就可以達到這個目的。例如,我們可以將前面的函
數重寫如下。
function concat(str1, str2, str3){
var result = str1 + str2;
if (typeof str3 == "string"){ // 恰當的比較
result += str3;
}
return result;
}
在這個重寫後的函數中, if 語句的條件會基於比較返回一個布爾值。這個函數相對可靠得多,不
容易受非正常值的影響。
2. 數據類型錯誤
JavaScript 是鬆散類型的,也就是說,在使用變量和函數參數之前,不會對它們進行比較以確保它
們的數據類型正確。 爲了保證不會發生數據類型錯誤, 只能依靠開發人員編寫適當的數據類型檢測代碼。
在將預料之外的值傳遞給函數的情況下,最容易發生數據類型錯誤。
在前面的例子中,通過檢測第三個參數可以確保它是一個字符串,但是並沒有檢測另外兩個參數。
如果該函數必須要返回一個字符串,那麼只要給它傳入兩個數值,忽略第三個參數,就可以輕易地導致
它的執行結果錯誤。類似的情況也存在於下面這個函數中。
//不安全的函數,任何非字符串值都會導致錯誤
function getQueryString(url){
var pos = url.indexOf("?");
if (pos > -1){
return url.substring(pos +1);
}
return "";
}
這個函數的用意是返回給定 URL 中的查詢字符串。爲此,它首先使用 indexOf() 尋找字符串中的
問號。如果找到了,利用 substring() 方法返回問號後面的所有字符串。這個例子中的兩個函數只能
操作字符串,因此只要傳入其他數據類型的值就會導致錯誤。而添加一條簡單的類型檢測語句,就可以
確保函數不那麼容易出錯。
function getQueryString(url){
if (typeof url == "string"){ // 通過檢查類型確保安全
var pos = url.indexOf("?");
if (pos > -1){
return url.substring(pos +1);
}
}
return "";
}
重寫後的這個函數首先檢查了傳入的值是不是字符串。這樣,就確保了函數不會因爲接收到非字符
串值而導致錯誤。

前一節提到過,在流控制語句中使用非布爾值作爲條件很容易導致類型轉換錯誤。同樣,這樣做也
經常會導致數據類型錯誤。來看下面的例子。
//不安全的函數,任何非數組值都會導致錯誤
function reverseSort(values){
if (values){ //絕對不要這樣!!!
values.sort();
values.reverse();
}
}
這個 reverseSort() 函數可以將數組反向排序,其中用到了 sort() 和 reverse() 方法。對於 if
語句中的控制條件而言,任何會轉換爲 true 的非數組值都會導致錯誤。另一個常見的錯誤就是將參數
與 null 值進行比較,如下所示。
//不安全的函數,任何非數組值都會導致錯誤
function reverseSort(values){
if (values != null){ // 絕對不要這樣!!!
values.sort();
values.reverse();
}
}
與 null 進行比較只能確保相應的值不是 null 和 undefined (這就相當於使用相等和不相等操
作) 。要確保傳入的值有效,僅檢測 null 值是不夠的;因此,不應該使用這種技術。同樣,我們也不
推薦將某個值與 undefined 作比較。
另一種錯誤的做法,就是隻針對要使用的某一個特性執行特性檢測。來看下面的例子。
//還是不安全,任何非數組值都會導致錯誤
function reverseSort(values){
if (typeof values.sort == "function"){ // 絕對不要這樣!!!
values.sort();
values.reverse();
}
}
在這個例子中,代碼首先檢測了參數中是否存在 sort() 方法。這樣,如果傳入一個包含 sort()
方法的對象(而不是數組)當然也會通過檢測,但在調用 reverse() 函數時可能就會出錯了。在確切
知道應該傳入什麼類型的情況下,最好是使用 instanceof 來檢測其數據類型,如下所示。
//安全,非數組值將被忽略
function reverseSort(values){
if (values instanceof Array){ // 問題解決了
values.sort();
values.reverse();
}
}
最後一個 reverseSort() 函數是安全的:它檢測了 values ,以確保這個參數是 Array 類型的實
例。這樣一來,就可以保證函數忽略任何非數組值。
大體上來說, 基本類型的值應該使用 typeof 來檢測, 而對象的值則應該使用 instanceof 來檢測。
根據使用函數的方式,有時候並不需要逐個檢測所有參數的數據類型。但是,面向公衆的 API 則必須無
條件地執行類型檢查,以確保函數始終能夠正常地執行。


3. 通信錯誤
隨着 Ajax 編程的興起(第 21 章討論 Ajax) ,Web 應用程序在其生命週期內動態加載信息或功能,
已經成爲一件司空見慣的事。不過,JavaScript 與服務器之間的任何一次通信,都有可能會產生錯誤。
第一種通信錯誤與格式不正確的 URL 或發送的數據有關。最常見的問題是在將數據發送給服務器
之前, 沒有使用 encodeURIComponent() 對數據進行編碼。 例如, 下面這個 URL 的格式就是不正確的:
http://www.yourdomain.com/?redir=http://www.someotherdomain.com?a=b&c=d
針對 "redir=" 後面的所有字符串調用 encodeURIComponent() 就可以解決這個問題, 結果將產生
如下字符串:
http://www.yourdomain.com/?redir=http%3A%2F%2Fwww.someotherdomain.com%3Fa%3Db%26c%3Dd
對於查詢字符串,應該記住必須要使用 encodeURIComponent() 方法。爲了確保這一點,有時候
可以定義一個處理查詢字符串的函數,例如:
function addQueryStringArg(url, name, value){
if (url.indexOf("?") == -1){
url += "?";
} else {
url += "&";
}
url += encodeURIComponent(name) + "=" + encodeURIComponent(value);
return url;
}
這個函數接收三個參數:要追加查詢字符串的 URL、參數名和參數值。如果傳入的 URL 不包含問
號,還要給它添加問號;否則,就要添加一個和號,因爲有問號就意味着有其他查詢字符串。然後,再
將經過編碼的查詢字符串的名和值添加到 URL 後面。可以像下面這樣使用這個函數:
var url = "http://www.somedomain.com";
var newUrl = addQueryStringArg(url, "redir",
"http://www.someotherdomain.com?a=b&c=d");
alert(newUrl);
使用這個函數而不是手工構建 URL,可以確保編碼正確並避免相關錯誤。
另外,在服務器響應的數據不正確時,也會發生通信錯誤。第 10 章曾經討論過動態加載腳本和動
態加載樣式, 運用這兩種技術都有可能遇到資源不可用的情況。 在沒有返回相應資源的情況下, Firefox、
Chrome 和 Safari 會默默地失敗,IE 和 Opera 則都會報錯。然而,對於使用這兩種技術產生的錯誤,很
難判斷和處理。在某些情況下,使用 Ajax 通信可以提供有關錯誤狀態的更多信息。
在使用 Ajax 通信的情況下,也可能會發生通信錯誤。相關的問題和錯誤將在第
21 章討論。
17.2.6 區分致命錯誤和非致命錯誤
任何錯誤處理策略中最重要的一個部分,就是確定錯誤是否致命。對於非致命錯誤,可以根據下列
一或多個條件來確定:

  不影響用戶的主要任務;
  隻影響頁面的一部分;
  可以恢復;
  重複相同操作可以消除錯誤。

本質上,非致命錯誤並不是需要關注的問題。例如,Yahoo! Mail(http://mail.yahoo.com)有一項功
能,允許用戶在其界面上發送手機短信。如果由於某種原因,發不了手機短信了,那也不算是致命錯誤,
因爲並不是應用程序的主要功能有問題。用戶使用 Yahoo! Mail 主要是爲了查收和撰寫電子郵件。只在
這個主要功能正常,就沒有理由打斷用戶。沒有必要因爲發生了非致命錯誤而對用戶給出提示——可以
把頁面中受到影響的區域替換掉, 比如替換成說明相應功能無法使用的消息。 但是, 如果因此打斷用戶,
那確實沒有必要。
致命錯誤,可以通過以下一或多個條件來確定:
  應用程序根本無法繼續運行;
  錯誤明顯影響到了用戶的主要操作;
  會導致其他連帶錯誤。

要想採取適當的措施,必須要知道 JavaScript 在什麼情況下會發生致命錯誤。在發生致命錯誤時,
應該立即給用戶發送一條消息,告訴他們無法再繼續手頭的事情了。假如必須刷新頁面才能讓應用程序
正常運行,就必須通知用戶,同時給用戶提供一個點擊即可刷新頁面的按鈕。
區分非致命錯誤和致命錯誤的主要依據,就是看它們對用戶的影響。設計良好的代碼,可以做到應
用程序某一部分發生錯誤不會不必要地影響另一個實際上毫不相干的部分。例如,My Yahoo!
(http://my.yahoo.com) 的個性化主頁上包含了很多互不依賴的模塊。 如果每個模塊都需要通過 JavaScript
調用來初始化,那麼你可能會看到類似下面這樣的代碼:
for (var i=0, len=mods.length; i < len; i++){
mods[i].init(); //可能會導致致命錯誤
}
表面上看, 這些代碼沒什麼問題: 依次對每個模塊調用 init() 方法。 問題在於, 任何模塊的 init()
方法如果出錯,都會導致數組中後續的所有模塊無法再進行初始化。從邏輯上說,這樣編寫代碼沒有什
麼意義。畢竟,每個模塊相互之間沒有依賴關係,各自實現不同功能。可能會導致致命錯誤的原因是代
碼的結構。不過,經過下面這樣修改,就可以把所有模塊的錯誤變成非致命的:
for (var i=0, len=mods.length; i < len; i++){
try {
mods[i].init();
} catch (ex) {
//在這裏處理錯誤
}
}
通過在 for 循環中添加 try-catch 語句,任何模塊初始化時出錯,都不會影響其他模塊的初始化。
在以上重寫的代碼中,如果有錯誤發生,相應的錯誤將會得到獨立的處理,並不會影響到用戶的體驗。
17.2.7 把錯誤記錄到服務器
開發 Web 應用程序過程中的一種常見的做法,就是集中保存錯誤日誌,以便查找重要錯誤的原因。

例如數據庫和服務器錯誤都會定期寫入日誌,而且會按照常用 API 進行分類。在複雜的 Web 應用程序
中,我們同樣推薦你把 JavaScript 錯誤也回寫到服務器。換句話說,也要將這些錯誤寫入到保存服務器
端錯誤的地方, 只不過要標明它們來自前端。 把前後端的錯誤集中起來, 能夠極大地方便對數據的分析。
要建立這樣一種 JavaScript 錯誤記錄系統,首先需要在服務器上創建一個頁面(或者一個服務器入
口點) ,用於處理錯誤數據。這個頁面的作用無非就是從查詢字符串中取得數據,然後再將數據寫入錯
誤日誌中。這個頁面可能會使用如下所示的函數:
function logError(sev, msg){
var img = new Image();
img.src = "log.php?sev=" + encodeURIComponent(sev) + "&msg=" +
encodeURIComponent(msg);
}
這個 logError() 函數接收兩個參數:表示嚴重程度的數值或字符串(視所用系統而異)及錯誤消
息。其中,使用了 Image 對象來發送請求,這樣做非常靈活,主要表現如下幾方面。
  所有瀏覽器都支持 Image 對象,包括那些不支持 XMLHttpRequest 對象的瀏覽器。
  可以避免跨域限制。通常都是一臺服務器要負責處理多臺服務器的錯誤,而這種情況下使用
XMLHttpRequest 是不行的。
  在記錄錯誤的過程中出問題的概率比較低。大多數 Ajax 通信都是由 JavaScript 庫提供的包裝函
數來處理的,如果庫代碼本身有問題,而你還在依賴該庫記錄錯誤,可想而知,錯誤消息是不
可能得到記錄的。
只要是使用 try-catch 語句,就應該把相應錯誤記錄到日誌中。來看下面的例子。
for (var i=0, len=mods.length; i < len; i++){
try {
mods[i].init();
} catch (ex){
logError("nonfatal", "Module init failed: " + ex.message);
}
}
在這裏,一旦模塊初始化失敗,就會調用 logError() 。第一個參數是 "nonfatal" (非致命) ,表
示錯誤的嚴重程度。第二個參數是上下文信息加上真正的 JavaScript 錯誤消息。記錄到服務器中的錯誤
消息應該儘可能多地帶有上下文信息,以便鑑別導致錯誤的真正原因。
17.3 調試技術
在不那麼容易找到 JavaScript 調試程序的年代,開發人員不得不發揮自己的創造力,通過各種方法
來調試自己的代碼。結果,就出現了以這樣或那樣的方式置入代碼,從而輸出調試信息的做法。其中,
最常見的做法就是在要調試的代碼中隨處插入 alert() 函數。 但這種做法一方面比較麻煩 (調試之後還
需要清理) ,另一方面還可能引入新問題(想象一下把某個 alert() 函數遺留在產品代碼中的結果) 。如
今,已經有了很多更好的調試工具,因此我們也不再建議在調試中使用 alert() 了。
17.3.1 將消息記錄到控制檯
IE8、Firefox、Opera、Chrome 和 Safari 都有 JavaScript 控制檯,可以用來查看 JavaScript 錯誤。而

且,在這些瀏覽器中,都可以通過代碼向控制檯輸出消息。對 Firefox 而言,需要安裝 Firebug
(www.getfirebug.com) ,因爲 Firefox 要使用 Firebug 的控制檯。對 IE8、Firefox、Chrome和 Safari 來說,
則可以通過 console 對象向 JavaScript 控制檯中寫入消息,這個對象具有下列方法。
 error(message) :將錯誤消息記錄到控制檯
 info(message) :將信息性消息記錄到控制檯
 log(message) :將一般消息記錄到控制檯
 warn(message) :將警告消息記錄到控制檯
在 IE8、Firebug、Chrome 和 Safari 中,用來記錄消息的方法不同,控制檯中顯示的錯誤消息也不
一樣。錯誤消息帶有紅色圖標,而警告消息帶有黃色圖標。以下函數展示了使用控制檯輸出消息的一
個示例。
function sum(num1, num2){
console.log("Entering sum(), arguments are " + num1 + "," + num2);
console.log("Before calculation");
var result = num1 + num2;
console.log("After calculation");
console.log("Exiting sum()");
return result;
}
在調用這個 sum() 函數時,控制檯中會出現一些消息,可以用來輔助調試。在 Safari 中,通過
“Develop” (開發)菜單可以打開其 JavaScript 控制檯(前面討論過) ;在 Chrome 中,單擊“Control this
page” (控制當前頁)按鈕並選擇“Developer” (開發人員)和“JavaScript console” (JavaScript 控制檯)
即可; 而在 Firefox 中, 要打開控制檯需要單擊 Firefox 狀態欄右下角的圖標。 IE8 的控制檯是其 Developer
Tools(開發人員工具)擴展的一部分,通過“Tools” (工具)菜單可以找到,其控制檯在“Script” (腳
本)選項卡中。
Opera 10.5 之前的版本中,JavaScript 控制檯可以通過 opera.postError() 方法來訪問。這個方法
接受一個參數,即要寫入到控制檯中的參數,其用法如下。
function sum(num1, num2){
opera.postError("Entering sum(), arguments are " + num1 + "," + num2);
opera.postError("Before calculation");
var result = num1 + num2;
opera.postError("After calculation");
opera.postError("Exiting sum()");
return result;
}
別看 opera.postError() 方法的名字好像是隻能輸出錯誤,但實際上能通過它向 JavaScript 控制
臺中寫入任何信息。
還有一種方案是使用 LiveConnect,也就是在 JavaScript 中運行 Java 代碼。Firefox、Safari 和 Opera
都支持 LiveConnect,因此可以操作 Java 控制檯。例如,通過下列代碼就可以在 JavaScript 中把消息寫
入到 Java 控制檯。
java.lang.System.out.println("Your message");

可以用這行代碼替代 console.log() 或 opera.postError() ,如下所示。
function sum(num1, num2){
java.lang.System.out.println("Entering sum(), arguments are " + num1 + "," + num2);
java.lang.System.out.println("Before calculation");
var result = num1 + num2;
java.lang.System.out.println("After calculation");
java.lang.System.out.println("Exiting sum()");
return result;
}
如果系統設置恰當, 可以在調用 LiveConnect 時就立即顯示 Java 控制檯。 在 Firefox 中, 通過 “Tools”
(工具)菜單可以打開 Java 控制檯;在 Opera 中,要打開 Java 控制檯,可以選擇菜單“Tools” (工具)
及“Advanced” (高級) 。Safari 沒有內置對 Java 控制檯的支持,必須單獨運行。
不存在一種跨瀏覽器向 JavaScript 控制檯寫入消息的機制,但下面的函數倒可以作爲統一的接口。
function log(message){
if (typeof console == "object"){
console.log(message);
} else if (typeof opera == "object"){
opera.postError(message);
} else if (typeof java == "object" && typeof java.lang == "object"){
java.lang.System.out.println(message);
}
}
ConsoleLoggingExample01.htm
這個 log() 函數檢測了哪個 JavaScript 控制檯接口可用,然後使用相應的接口。可以在任何瀏覽器
中安全地使用這個函數,不會導致任何錯誤,例如:
function sum(num1, num2){
log("Entering sum(), arguments are " + num1 + "," + num2);
log("Before calculation");
var result = num1 + num2;
log("After calculation");
log("Exiting sum()");
return result;
}
ConsoleLoggingExample01.htm
向 JavaScript 控制檯中寫入消息可以輔助調試代碼,但在發佈應用程序時,還必須要移除所有消息。
在部署應用程序時,可以通過手工或通過特定的代碼處理步驟來自動完成清理工作。
記錄消息要比使用 alert() 函數更可取, 因爲警告框會阻斷程序的執行, 而在測
定異步處理對時間的影響時,使用警告框會影響結果。

將消息記錄到當前頁面
另一種輸出調試消息的方式,就是在頁面中開闢一小塊區域,用以顯示消息。這個區域通常是一個
元素,而該元素可以總是出現在頁面中,但僅用於調試目的;也可以是一個根據需要動態創建的元素。
例如,可以將 log() 函數修改爲如下所示:
function log(message){
var console = document.getElementById("debuginfo");
if (console === null){
console = document.createElement("div");
console.id = "debuginfo";
console.style.background = "#dedede";
console.style.border = "1px solid silver";
console.style.padding = "5px";
console.style.width = "400px";
console.style.position = "absolute";
console.style.right = "0px";
console.style.top = "0px";
document.body.appendChild(console);
}
console.innerHTML += "<p>" + message + "</p>";
}
PageLoggingExample01.htm
這個修改後的 log() 函數首先檢測是否已經存在調試元素,如果沒有則會新創建一個 <div> 元素,
併爲該元素應用一些樣式,以便與頁面中的其他元素區別開。然後,又使用 innerHTML 將消息寫入到
這個 <div> 元素中。結果就是頁面中會有一小塊區域顯示錯誤消息。這種技術在不支持 JavaScript 控制
臺的 IE7 及更早版本或其他瀏覽器中十分有用。
與把錯誤消息記錄到控制檯相似,把錯誤消息輸出到頁面的代碼也要在發佈前
刪除。
17.3.3 拋出錯誤
如前所述,拋出錯誤也是一種調試代碼的好辦法。如果錯誤消息很具體,基本上就可以把它當作確
定錯誤來源的依據。但這種錯誤消息必須能夠明確給出導致錯誤的原因,才能省去其他調試操作。來看
下面的函數:
function divide(num1, num2){
return num1 / num2;
}
這個簡單的函數計算兩個數的除法,但如果有一個參數不是數值,它會返回 NaN 。類似這樣簡單的
計算如果返回 NaN ,就會在 Web 應用程序中導致問題。對此,可以在計算之前,先檢測每個參數是否都
是數值。例如:
function divide(num1, num2){
if (typeof num1 != "number" || typeof num2 != "number"){
throw new Error("divide(): Both arguments must be numbers.");
}
return num1 / num2;
}

在此,如果有一個參數不是數值,就會拋出錯誤。錯誤消息中包含了函數的名字,以及導致錯誤的
真正原因。瀏覽器只要報告了這個錯誤消息,我們就可以立即知道錯誤來源及問題的性質。相對來說,
這種具體的錯誤消息要比那些泛泛的瀏覽器錯誤消息更有用。
對於大型應用程序來說,自定義的錯誤通常都使用 assert() 函數拋出。這個函數接受兩個參數,
一個是求值結果應該爲 true 的條件, 另一個是條件爲 false 時要拋出的錯誤。 以下就是一個非常基本
的 assert() 函數。
function assert(condition, message){
if (!condition){
throw new Error(message);
}
}
AssertExample01.htm
可以用這個 assert() 函數代替某些函數中需要調試的 if 語句,以便輸出錯誤消息。下面是使用
這個函數的例子。
function divide(num1, num2){
assert(typeof num1 == "number" && typeof num2 == "number",
"divide(): Both arguments must be numbers.");
return num1 / num2;
}
AssertExample01.htm
可見,使用 assert() 函數可以減少拋出錯誤所需的代碼量,而且也比前面的代碼更容易看懂。
17.4 常見的 IE 錯誤
多年以來,IE 一直都是最難於調試 JavaScript 錯誤的瀏覽器。IE 給出的錯誤消息一般很短又語焉不
詳,而且上下文信息也很少,有時甚至一點都沒有。但作爲用戶最多的瀏覽器,如何看懂 IE 給出的錯
誤也是最受關注的。下面幾小節將分別探討一些在 IE 中難於調試的 JavaScript 錯誤。
17.4.1 操作終止
在 IE8 之前的版本中,存在一個相對於其他瀏覽器而言,最令人迷惑、討厭,也最難於調試的錯誤:
操作終止(operation aborted) 。在修改尚未加載完成的頁面時,就會發生操作終止錯誤。發生錯誤時,
會出現一個模態對話框,告訴你“操作終止。 ”單擊確定(OK)按鈕,則卸載整個頁面,繼而顯示一張
空白屏幕;此時要進行調試非常困難。下面的示例將會導致操作終止錯誤。
<!DOCTYPE html>
<html>
<head>
<title>Operation Aborted Example</title>
</head>
<body>
<p>The following code should cause an Operation Aborted error in IE versions
prior to 8.</p>

<div>
<script type="text/javascript">
document.body.appendChild(document.createElement("div"));
</script>
</div>
</body>
</html>
OperationAbortedExample01.htm
這個例子中存在的問題是:JavaScript 代碼在頁面尚未加載完畢時就要修改 document.body ,而且
<script> 元素還不是 <body> 元素的直接子元素。準確一點說,當 <script> 節點被包含在某個元素中,
而且 JavaScript 代碼又要使用 appendChild() 、 innerHTML 或其他 DOM 方法修改該元素的父元素或
祖先元素時,將會發生操作終止錯誤(因爲只能修改已經加載完畢的元素) 。
要避免這個問題,可以等到目標元素加載完畢後再對它進行操作,或者使用其他操作方法。例如,
爲 document.body 添加一個絕對定位在頁面上的覆蓋層,就是一種非常常見的操作。通常,開發人員
都是使用 appendChild() 方法來添加這個元素的, 但換成使用 insertBefore() 方法也很容易。 因此,
只要修改前面例子中的一行代碼,就可以避免操作終止錯誤。
<!DOCTYPE html>
<html>
<head>
<title>Operation Aborted Example</title>
</head>
<body>
<p>The following code should not cause an Operation Aborted error in IE
versions prior to 8.</p>
<div>
<script type="text/javascript">
document.body.insertBefore(document.createElement("div"),
document.body.firstChild);
</script>
</div>
</body>
</html>
OperationAbortedExample02.htm
在這個例子中,新的 <div> 元素被添加到 document.body 的開頭部分而不是末尾。因爲完成這一
操作所需的所有信息在腳本運行時都是已知的,所以這不會引發錯誤。
除了改變方法之外, 還可以把 <script> 元素從包含元素中移出來, 直接作爲 <body> 的子元素。 例如:
<!DOCTYPE html>
<html>
<head>
<title>Operation Aborted Example</title>
</head>
<body>
<p>The following code should not cause an Operation Aborted error in IE
versions prior to 8.</p>
<div>
</div>
<script type="text/javascript">
document.body.appendChild(document.createElement("div"));
</script>

</body>
</html>
OperationAbortedExample03.htm
這一次也不會發生錯誤,因爲腳本修改的是它的直接父元素,而不再是間接的祖先元素。
在同樣的情況下,IE8不再拋出操作終止錯誤,而是拋出常規的 JavaScript錯誤,帶有如下錯誤消息:
HTML Parsing Error: Unable to modify the parent container element before the child
element is closed (KB927917).
不過,雖然瀏覽器拋出的錯誤不同,但解決方案仍然是一樣的。
17.4.2 無效字符
根據語法,JavaScript 文件必須只包含特定的字符。在 JavaScript 文件中存在無效字符時,IE 會拋出
無效字符(invalid character)錯誤。所謂無效字符,就是 JavaScript 語法中未定義的字符。例如,有一
個很像減號但卻由 Unicode 值 8211 表示的字符( \u2013 ) ,就不能用作常規的減號(ASCII 編碼爲 45) ,
因爲 JavaScript 語法中沒有定義該字符。這個字符通常是在 Word 文檔中自動插入的。如果你的代碼是
從 Word 文檔中複製到文本編輯器中,然後又在 IE 中運行的,那麼就可能會遇到無效字符錯誤。其他瀏
覽器對無效字符做出的反應與 IE 類似,Firefox 會拋出非法字符(illegal character)錯誤,Safari 會報告
發生了語法錯誤,而 Opera 則會報告發生了 ReferenceError (引用錯誤) ,因爲它會將無效字符解釋
爲未定義的標識符。
17.4.3 未找到成員
如前所述,IE 中的所有 DOM 對象都是以 COM 對象,而非原生 JavaScript 對象的形式實現的。這
會導致一些與垃圾收集相關的非常奇怪的行爲。IE 中的未找到成員(Member not found)錯誤,就是由
於垃圾收集例程配合錯誤所直接導致的。
具體來說,如果在對象被銷燬之後,又給該對象賦值,就會導致未找到成員錯誤。而導致這個錯誤
的,一定是 COM對象。發生這個錯誤的最常見情形是使用 event 對象的時候。IE 中的 event 對象是
window 的屬性,該對象在事件發生時創建,在最後一個事件處理程序執行完畢後銷燬。假設你在一個
閉包中使用了 event 對象,而該閉包不會立即執行,那麼在將來調用它並給 event 的屬性賦值時,就
會導致未找到成員錯誤,如下面的例子所示。
document.onclick = function(){
var event = window.event;
setTimeout(function(){
event.returnValue = false; //未找到成員錯誤
}, 1000);
};
在這段代碼中,我們將一個單擊事件處理程序指定給了文檔。在事件處理程序中, window.event
被保存在 event 變量中。然後,傳入 setTimeout() 中的閉包裏又包含了 event 變量。當單擊事件處
理程序執行完畢後, event 對象就會被銷燬, 因而閉包中引用對象的成員就成了不存在的了。 換句話說,
由於不能在 COM 對象被銷燬之後再給其成員賦值,在閉包中給 returnValue 賦值就會導致未找到成
員錯誤。

17.4.4 未知運行時錯誤
當使用 innerHTML 或 outerHTML 以下列方式指定 HTML 時, 就會發生未知運行時錯誤 (Unknown
runtime error) :一是把塊元素插入到行內元素時,二是訪問表格任意部分( <table> 、 <tbody> 等)的
任意屬性時。例如,從技術角度說, <span> 標籤不能包含 <div> 之類的塊級元素,因此下面的代碼就會
導致未知運行時錯誤:
span.innerHTML = "<div>Hi</div>"; //這裏,span 包含了<div>元素
在遇到把塊級元素插入到不恰當位置的情況時,其他瀏覽器會嘗試糾正並隱藏錯誤,而 IE 在這一
點上反倒很較真兒。
17.4.5 語法錯誤
通常,只要 IE 一報告發生了語法錯誤(syntax error) ,都可以很快找到錯誤的原因。這時候,原因
可能是代碼中少了一個分號,或者花括號前後不對應。然而,還有一種原因不十分明顯的情況需要格外
注意。
如果你引用了外部的 JavaScript 文件,而該文件最終並沒有返回 JavaScript 代碼,IE 也會拋出語法
錯誤。例如, <script> 元素的 src 特性指向了一個 HTML 文件,就會導致語法錯誤。報告語法錯誤的
位置時,通常都會說該錯誤位於腳本第一行的第一個字符處。Opera 和 Safari 也會報告語法錯誤,但它
們會給出導致問題的外部文件的信息;IE 就不會給出這個信息,因此就需要我們自己重複檢查一遍引用
的外部 JavaScript文件。但 Firefox會忽略那些被當作 JavaScript內容嵌入到文檔中的非 JavaScript文件中的
解析錯誤。
在服務器端組件動態生成 JavaScript 的情況下,比較容易出現這種錯誤。很多服務器端語言都會在
發生運行時錯誤時,向輸出中插入 HTML 代碼,而這種包含 HTML 的輸出很容易就會違反 JavaScript
語法。如果在追查語法錯誤時遇到了麻煩,我們建議你再仔細檢查一遍引用的外部文件,確保這些文件
中沒有包含服務器因錯誤而插入到其中的 HTML。
17.4.6 系統無法找到指定資源
系統無法找到指定資源(The system cannot locate the resource specified)這種說法,恐怕要算是 IE
給出的最有價值的錯誤消息了。 在使用 JavaScript請求某個資源 URL, 而該 URL的長度超過了 IE對 URL
最長不能超過 2083 個字符的限制時,就會發生這個錯誤。 IE 不僅限制 JavaScript 中使用的 URL 的長度,
而且也限制用戶在瀏覽器自身中使用的 URL 長度 (其他瀏覽器對 URL 的限制沒有這麼嚴格) 。 IE 對 URL
路徑還有一個不能超過 2048 個字符的限制。下面的代碼將會導致錯誤。
function createLongUrl(url){
var s = "?";
for (var i=0, len=2500; i < len; i++){
s += "a";
}
return url + s;
}
var x = new XMLHttpRequest();

x.open("get", createLongUrl("http://www.somedomain.com/"), true);
x.send(null);
LongURLErrorExample01.htm
在這個例子中, XMLHttpRequest 對象試圖向一個超出最大長度限制的 URL 發送請求。在調用
open() 方法時,就會發生錯誤。避免這個問題的辦法,無非就是通過給查詢字符串參數起更短的名字,
或者減少不必要的數據,來縮短查詢字符串的長度。另外,還可以把請求方法改爲 POST ,通過請求體
而不是查詢字符串來發送數據。

src: http://blog.csdn.net/luozhonghua2014/article/details/45607667

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章