作者:bubkoo的部落格
網址:http://bubkoo.com/2015/01/25/Strange-JavaScript-Errors-and-How-to-Fix-Them/
點選“閱讀原文”可檢視本文網頁版
除錯 JavaScript 也許是一場噩夢:一些錯誤非常難理解,並且給出的錯誤行號並不是總是很有幫助。如果有一個串列,列舉這些錯誤的意思和如何修複它們,將對我們非常有幫助。
本文列舉了 JavaScript 中一些奇怪的錯誤。對於相同的錯誤不同的瀏覽器可能給出不同的提示,所以分別給出了不同的例子。
如何閱讀錯誤
進入正題之前,我們先快速分析一下錯誤訊息的結構,這對我們理解錯誤訊息非常有用,同時也將有助於你理解那些沒有在本文中列舉的錯誤。
Chrome 中一個典型的錯誤看起來像這樣:
Uncaught TypeError: undefined is not a function
該錯誤的結構如下:
- Uncaught TypeError: 該部分並不是很有用。Uncaught 表示該錯誤沒有被 catch 陳述句捕獲,TypeError 是錯誤名。
- undefined is not a function: 是訊息體,需要從字面上理解。例如本例中,它的字面意思是,程式碼嘗試將 undefined 當作函式使用。
其他基於 webkit 的瀏覽器,比如 Safari,錯誤訊息與 Chrome 基本一樣。Firefox 的錯誤訊息與上面非常相似,但並不總是都包含第一部分,最近版本的 IE 的錯誤訊息也比 Chrome 的簡單,但在這裡,更簡單並不意味著更好。
下麵看看我們經常會遇到的一些錯誤。
Uncaught TypeError: undefined is not a function
同類錯誤:
- number is not a function
- object is not a function
- string is not a function
- Unhandled Error: ‘foo’ is not a function
- Function Expected
嘗試將一個值(value)當作函式使用,但該值並不是一個函式。例如:
var foo = undefined;
foo();
這個錯誤很常見,當呼叫物件中的一個方法,但寫錯了方法名:
var x = document.getElementByID(‘foo’);
訪問物件中不存在的屬性時將傳回 undefined,上面程式碼就將出現該錯誤。
其他類似的錯誤,比如“number is not a function”發生在嘗試將一個 Number 當作函式使用時。
如何修複:確保函式名正確。對於該錯誤,行號通常準確地指向了錯誤發生的位置。
Uncaught ReferenceError: Invalid left-hand side in assignment
同類錯誤:
- Uncaught exception: ReferenceError: Cannot assign to ‘functionCall()’
- Uncaught exception: ReferenceError: Cannot assign to ‘this’
當嘗試給一個不能被賦值的變數賦值時將發生該錯誤。看下麵的典型例子:
if(doSomething() = ‘somevalue’)
在上面例子中,開發人員不小心將 == 寫成了 =,錯誤訊息“left-hand side in assignment”指等號左邊包含不能被賦值的變數。
如何修複:確保不給函式函式的傳回值或 this 關鍵字賦值。
Uncaught TypeError: Converting circular structure to JSON
同類錯誤:
- Uncaught exception: TypeError: JSON.stringify: Not an acyclic Object
- TypeError: cyclic object value
- Circular reference in value argument not supported
該錯誤總是發生在使用 JSON.stringify 序列化一個存在迴圈取用的物件時。
var a = { };
var b = { a: a };
a.b = b;
JSON.stringify(a);
由於上面 a 和 b 兩個物件都彼此相互取用,結果導致物件不能被轉換為 JSON 字串。
如何修複:移除將要被轉換為 JSON 字串物件內部的迴圈取用。
Unexpected token ;
同類錯誤:
- Expected )
- missing ) after argument list
通常發生在缺少括號或分號時。
該錯誤中所謂的符號(token)可以多種多樣,如“Unexpected token ]”或“Expected {”等等。
如何修複:該錯誤提示的行號有時並不能指向正確的位置,這增加了修複難度。
錯誤資訊中包含“[ ] { } ( )”時,通常是因為缺少配對的部分,檢查所有括號,保證都是配對的。這種情況下,行號通常指向了其他位置,問不是錯誤的位置。
異常的 / 和正則運算式有關,行號指向了正確的位置。
異常的 ; 通常發生在物件、陣列或函式呼叫時引數串列內部包含 ;,行號也指向了正確的位置。
Uncaught SyntaxError: Unexpected token ILLEGAL
同類錯誤:
- Unterminated String Literal
- Invalid Line Terminator
字串字面量缺少閉合的引號。
如何修複:確保所有字串都包含閉合的引號。
Uncaught TypeError: Cannot read property ‘foo’ of null, Uncaught TypeError: Cannot read property ‘foo’ of undefined
同類錯誤:
- TypeError: someVal is null
- Unable to get property ‘foo’ of undefined or null reference
嘗試將 null 過 undefined 作為一個物件使用,例如:
var someVal = null;
console.log(someVal.foo);
如何修複:通常是由於書寫失誤導致,確保錯誤提示的行號附近的變數都是書寫正確的。
Uncaught TypeError: Cannot set property ‘foo’ of null, Uncaught TypeError: Cannot set property ‘foo’ of undefined
同類錯誤:
- TypeError: someVal is undefined
- Unable to set property ‘foo’ of undefined or null reference
嘗試為值為 null 或 undefined 的物件的屬性賦值。
var someVal = null;
someVal.foo = 1;
如何修複:這也通常是由於書寫錯誤導致,檢查錯誤提示的行號附近的變數名是否正確。
Uncaught RangeError: Maximum call stack size exceeded
同類錯誤:
- Uncaught exception: RangeError: Maximum recursion depth exceeded
- too much recursion
- Stack overflow
通常是由程式邏輯問題,導致了無限遞迴的函式呼叫。
如何修複:檢查函式的遞迴呼叫,確保函式不是無限遞迴的。
Uncaught URIError: URI malformed
同類錯誤:URIError: malformed URI sequence
無效的 decodeURIComponent 呼叫將導致該錯誤。
如何修複:確保行號所指位置的 decodeURIComponent 呼叫的引數正確。
XMLHttpRequest cannot load http://some/url/. No ‘Access-Control-Allow-Origin’ essay-header is present on the requested resource
同類錯誤:Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://some/url/
該錯誤總是由使用 XMLHttpRequest 時導致。
如何修複:確保請求的 url 滿足同源策略。
InvalidStateError: An attempt was made to use an object that is not, or is no longer, usable
同類錯誤:
- InvalidStateError
- DOMException code 11
該錯誤表示呼叫物件的方法時,物件的狀態不對。在使用 XMLHttpRequest 時,在其準備好之前嘗試呼叫其中的方法將導致該錯誤。
var xhr = new XMLHttpRequest();
xhr.setRequestHeader(‘Some-Header’, ‘val’);
上例中將導致錯誤,因為 setRequestHeader 方法只能在 xhr.open 之後呼叫。
如何修複:檢查行號指示的位置,確保程式碼執行在合適的時間,或在這之前新增必要的函式呼叫(比如xhr.open)。