Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
前端的 JavaScript 開發中,發現開發者對于錯誤異常的處理普遍都比較簡單粗暴,如果應用程序中缺少有效的錯誤處理和容錯機制,代碼的健壯性就無從談起。本文整理出了一些常見的錯誤異常處理的場景,旨在為前端的 JavaScript 錯誤異常處理提供一些基礎的指導。
先來簡單介紹一下 JavaScript 中的 Error 對象,通常 Error 對象由重要的兩部分組成,包含了 error.message 錯誤信息和 error.stack 錯誤追溯棧。
產生一個錯誤很簡單,比如在 foo.js 中直接調用一個不存在的 callback 函數。
// foo.js
function foo () {
callback();
}
foo();
此時通過 Chrome 瀏覽器的控制臺會展示如下的信息。
Uncaught ReferenceError: callback is not defined
at foo (foo.js:2)
at foo.js:5
其中 Uncaught ReferenceError: callback is not defined 就是 error.message 錯誤信息,而剩下的 at xxx 就是具體的錯誤追溯棧,在 Chrome 的控制臺中,對錯誤的展示進行了優化。
如果我們通過 window.onerror 來捕獲到該錯誤后將 Error 對象直接輸出到頁面中會展示出更原始的數據。
<!-- 展示錯誤的容器 -->
<textarea id="error"></textarea>
// 輸出錯誤
window.onerror=function (msg, url, line, col, err) {
document.getElementById('error').textContent=err.message + '\n\n' + err.stack;
};
原始的錯誤數據中會展示出錯誤追溯棧中的 Source URL。
callback is not defined
ReferenceError: callback is not defined
at foo (http://example.com/js-error/foo.js:2:5)
at http://example.com/js-error/foo.js:5:1
有了錯誤追溯棧,就能通過發生錯誤的文件 Source URL 和錯誤在代碼中的具體位置來快速定位到錯誤。
看起來好像很簡單,但實際的開發中如何有效的捕獲錯誤,如何有效的拋出錯誤都有一些需要注意的點,下面逐個的來講解。
前端在捕獲錯誤時都會通過綁定 window.onerror 事件來捕獲全局的 JavaScript 執行錯誤,標準的瀏覽器在響應該事件時會依次提供 5 個參數。
window.onerror=function(message, source, lineno, colno, error) { ... }
使用 window.addEventListener 也能綁定 error 事件,但是該事件函數的參數是一個 ErrorEvent 對象。
綁定 window.onerror 事件時,事件處理函數的第 5 個參數在低版本瀏覽中或 JS 資源跨域場景下可能不是 Error 對象。
在 Chrome 瀏覽器中如果頁面加載的 JS 資源文件中存在跨域的 script 標簽,在發生錯誤時會提示 Script error 而缺乏錯誤追溯棧。
window.onerror 在響應跨域 JavaScript 錯誤時缺乏錯誤追溯棧時的 arguments 對象如下:
[
'Script error.',
'',
0,
0,
null
]
為了正常的捕獲到跨域 JS 資源文件的錯誤,需要具備兩個條件: 1. 為 JS 資源文件增加 CORS 響應頭。 2. 通過 script 引用該 JS 文件時增加 crossorigin="anonymous" 的屬性,如果是動態加載的 JS,可以寫作 script.crossOrigin=true 。
window.onerror 能捕獲一些全局的 JavaScript 錯誤,但還有不少場景在全局是捕獲不到的。
window.onerror 能捕獲全局場景下的錯誤,如果已知一些程序的場景中可能會出現錯誤,這個時候一般會使用 try/catch 來進行捕獲。
但是在使用 try/catch 塊時無法捕獲異步錯誤,例如塊中使用了 setTimeout 。
try {
setTimeout(function () {
callTimeout(); // callTimeout 未定義,會拋錯
}, 1000);
}
catch (err) {
console.log('catch the error', err); // 不會被執行
}
try/catch 在處理 setTimeout 這類異步場景時是無效的,執行時仍會拋錯,catch 中的代碼不會被執行。
雖然在 try/catch 中沒有捕獲到,此時如果有綁定 window.onerror 則會被全局捕獲。
由此可見, try/catch 應該是只能捕獲 JS Event Loop 中同步的任務。
如果想正確的捕獲 setTimeout 中的錯誤,需要將 try/catch 塊寫到 setTimeout 的函數中。
setTimeout(function () {
try {
callTimeout(); // callTimeout 未定義,不會拋錯
}
catch (err) {
console.log('catch the error', err); // 將會被執行
}
}, 1000);
Promise 有自己的錯誤處理機制,通常 Promise 函數中的錯誤無法被全局捕獲。
var promise=new Promise(executor);
promise.then(onFulfilled, onRejected);
比較容易遺漏錯誤處理的地方有 executor 和 onFulfilled ,在這些函數中如果發生錯誤都不能被全局捕獲。
正確的捕獲 Promise 的錯誤,應該使用 Promise.prototype.catch 方法,意外的錯誤和使用 reject 主動捕獲的錯誤都會觸發 catch 方法。
catch 方法中通常會接收到一個 Error 對象,但是當調用 reject 函數時傳入的是一個非 Error 對象時,catch 方法也會接收到一個非 Error 對象,這里的 reject 和 throw 的表現是一樣的,所以在使用 reject 時,最好是傳入一個 Error 對象。
reject(
new Error('this is reject message')
);
值得注意的是,如果 Promise 的 executor 中存在 setTimeout 語句時, setTimeout 的報錯會被全局捕獲。
Async Function 和 Promise 一樣,發生錯誤不會被全局的 window.onerror 捕獲,所以在使用時如果有報錯,需要手動增加 try/catch 語句。
匿名函數的使用在 JavaScript 中很常見,但是當出現匿名函數的報錯時,在錯誤追溯棧中會以 anonymous 來標識錯誤,為了排查錯誤方便,可以將函數進行命名,或者使用函數的 displayName 屬性。
函數如果有 displayName 屬性,在錯誤棧中會展示該屬性值,如果用于命名重要的業務邏輯屬性,將有效幫助排查錯誤。
上面說了很多錯誤捕獲的注意點,如果要主動的拋錯,都會使用 throw 來拋錯,常見的幾種拋錯方法如下:
throw new Error('Problem description.') // 方法 1
throw Error('Problem description.') // 方法 2
throw 'Problem description.' // 方法 3
throw null // 方法 4
其中方法 1 和方法 2 的效果一樣,瀏覽器都能正確的展示錯誤追溯棧。方法 3 和方法 4 不推薦,雖然能拋錯,但是在拋錯的時候不能展示錯誤追溯棧。
try/catch 和 throw ,一個用來捕獲錯誤,一個用來拋出錯誤,如果兩個結合起來用通常等于脫了褲子放屁多此一舉,唯一有點用的是可以對錯誤信息進行再加工。
可以在 Chrome 控制臺中模擬出一個結合使用的實際場景。
try {
foo();
}
catch (err) {
err.message='Catch the error: ' + err.message;
throw Error(err);
}
由于在 catch 塊中又拋出了錯誤,所以該錯誤沒有被捕獲到,但此時錯誤信息經過了二次封裝。
Uncaught Error: ReferenceError: Catch the error: foo is not defined
通過對錯誤信息的二次封裝,可以增加一些有利于快速定位錯誤的額外信息。
原作者:雨夜帶刀's Blog
當javascript代碼中出現錯誤的時候,js引擎就會根據js的調用棧逐級尋找對應的catch,如果沒有找到相應的catch handler或catch handler本身又有error或者又拋出新的error,最后就會把這個error的處理交給瀏覽器,并顯示在錯誤控制臺中)顯示錯誤信息給訪問者。
是js提供的異常處理機制,用法如下:
try {
// 這段代碼從上往下運行,其中任何一個語句拋出異常該代碼塊就結束運行}
catch (e) {
// 如果try代碼塊中拋出了異常,catch代碼塊中的代碼就會被執行。
// e是一個局部變量,用來指向Error對象或者其他拋出的對象
}
finally {
//無論try中代碼是否有異常拋出(甚至是try代碼塊中有return語句),
//finally代碼塊中始終會被執行
}
顧名思義,典型的語法錯誤。
function foo{
}
if{}
Js代碼是從上往下依次執行,但是js引擎先解析代碼是否語法出錯,如果語法都解析不通過,那么顯而易見:一行代碼也不會執行,從而會在控制臺中輸出語法報錯:
syntaxError錯誤
變量未定義(也叫未聲明)指的是,當程序中使用了一個未定義的變量則會報錯。
如下代碼:
var foo=1
var bar=foo + n1
顯而易見,n1變量是未定義的就直接使用,從而會在控制臺中輸出如下錯誤:
XXX is not defined 變量未定義
TypeError錯誤指的是數據類型未正確使用。
例如一:
var foo=function(){
console.log('foo')
}
foo='hello world'
foo()
在某些邏輯下,foo本身存儲的是函數,但是誤把foo賦值了一個字符串或其它不是函數的數據類型,但是foo當作函數來調用了,則會報TypeError錯誤在控制臺中輸出:
TypeError,xxx is not a function
例如二:
未正確獲取元素,導致得到一個null而不是DOM節點對象后,綁定事件而引發的TypeError錯誤。
<script>
var oBtn=document.getElementById('btn')
//因為代碼從上往下解析的原因,還未解析到button標簽,返回為null。
//null是空對象,不能綁定任何屬性,onclick雖然是事件,
//但也是對象中屬性的一部分,所以報TypeError錯誤。
oBtn.onclick=function(){
console.log('bar')
}
</script>
<button id="btn">foo</button>
cannot set property 'onclick' of null
正確錯誤是把選擇元素的js代碼放置html標簽之后,也就是緊鄰 </body>標簽,或放在windo.onload事件中。
<script>
window.onload=function(){
var oBtn=document.getElementById('btn')
//因為代碼從上往下解析的原因,還未解析到button標簽,返回為null。
//null是空對象,不能綁定任何屬性,onclick雖然是事件,
//但也是對象中屬性的一部分,所以報TypeError錯誤。
oBtn.onclick=function(){
console.log('bar')
}
}
</script>
<button id="btn">foo</button>
首先,我們需要了解JSON是什么 ?
JSON(JavaScript Object Notation, JS 對象簡譜) 是一種輕量級的數據交換格式。它基于 ECMAScript (歐洲計算機協會制定的js規范)的一個子集,采用完全獨立于編程語言的文本格式來存儲和表示數據。簡潔和清晰的層次結構使得 JSON 成為理想的數據交換語言。
而它的定義規則和js中字面量聲明對象很像,所以很多初學者以為json就是js對象,其實這是錯誤的。
key : value
名稱/值包括字段名稱(在雙引號中),后面寫一個冒號,然后是值:
"name" : "foo"
前方高能~~~
談到這里,json數據從哪來呢?
在請求Ajax的時候,會從后臺服務器中拿到json數據,往往會把json解析成js對象。則前端工程師會用到JSON.parse方法。有時候前端也會定義JSON數據,如果語法不正確當轉成js對象時,則會報錯。如下代碼:
//var foo='{ name:"bar" }'//name未帶雙引號
var foo='{ "name":bar }'//bar未帶雙引號
var f=JSON.parse( foo )
token n in JSON at position
正確的JSON轉換js對象的方式如下:
var foo='{ "name":"bar","age":20 }'//20無需帶,則理解為數值類型
var f=JSON.parse( foo )
console.log( f ) //{name: "bar", age: 20} ,此時可以正確的把json轉換成js對象,
//通過 點 語法,也就是f.name和f.age訪問到具體的數據
以上是JavaScript中常見的錯誤,后期遇到會不斷更新,感謝小伙伴們的踴躍投稿和留言。
內容是《Web前端開發之Javascript視頻》的課件,請配合大師哥《Javascript》視頻課程學習。
錯誤處理對于web應用開發至關重要,任何javascript錯誤都有可能會導致網頁無法使用,因此作為開發人員,必須要及時處理有可能出現的錯誤;
從IE4.0之后,幾乎所有的瀏覽器都包含了一些基本的錯誤處理功能,但并沒有統一,后來,由ECMAscript添加了異常處理機制,也就是try…catch…finally結構以及throw操作;
錯誤處理的重要性: 好的錯誤處理技術可以讓腳本的開發、調試和部署更加流暢,能對代碼進行更好的控制;另外,JS缺乏標準的開發環境;
錯誤類型:
語法錯誤(syntax error):也稱為解析錯誤,發生在傳統編程語言的編譯解釋時;發生語法錯誤時,就會發生阻塞,也就是不能繼續執行代碼,但只是同一個線程中的代碼會受影響,其他線程中的代碼不受影響;
// Uncaught SyntaxError: Invalid or unexpected token
// 未捕獲的語法錯誤:無效或意外的標記
document.write("zeronetwork;
運行時錯誤(Runtime error):也稱為exception異常, 其發生在編譯期/解釋期后,此時,問題并不出現在代碼的語法上,而是在嘗試完成一個非法的操作;
<input type="button" value="單擊" onclick="handleClick()" />
<script>
// Uncaught ReferenceError: openMy is not defined
// 未捕獲的引用錯誤:未定義openMy
function handleClick(){
openMy();
}
</script>
錯誤報告:
因為每個瀏覽器都有自己的內置Javascript解釋程序,所以每種瀏覽器報告錯誤的方式都不同;有些是彈出錯誤信息,有些是把信息打印在控制臺中;
IE(windows): 默認情況下,會彈出包含錯誤細節的對話框,并詢問是否繼續執行頁面上的腳本;如果瀏覽器有調試器(如:Microsoft Script Debugger) ,此對話框會提供一個是調試還是忽略的選項;如果在IE設置中取消了”顯示錯誤”,那么會在頁面左下角顯示一個黃色的圖標;
注:如果JS代碼就在HTML里,顯示錯誤行是正確的,如果是外部的JS文件,則行號往往差一行,如第5行則為第4行;
Mozilla(所有平臺): 在控制臺中打印錯誤信息,并發出警告;其會報告三種類型的消息:錯誤、嚴格警告和消息等的;
Safari (MacOS):是對JavaScript錯誤和調試的支持最差,默認情況下,它對終端用戶不提供任何javascript錯誤報告;
錯誤處理:
Javascript提供了兩種處理錯誤的方式:
onerror事件處理函數:
window對象的onerror屬性是一個事件處理程序,頁面上出現異常時,error事件便在window對象上觸發,并把錯誤消息輸出到Javascript控制臺上,這種方式也稱為全局錯誤捕獲;如:
window.onload=function(){
show(); // 在onload事件中調用了一個不存在的函數
}
window.onerror=function(){
alert("出現錯誤");
return true;
}
獲取錯誤信息:
window.onerror事件處理程序在調用時可以傳5個參數,由這5個參數可以獲取詳細的錯誤信息;
window.onerror=function(sMessage, sUrl, sLine, sColumn, error){
console.log("Error:" + sMessage + " URL:" + sUrl + " Line:" + sLine + " Column:" + sColumn);
console.log(error);
return true;
}
onerror處理程序的返回值:
如果返回true,則阻止執行默認的事件處理程序,也就是將通知瀏覽器,事件處理程序已經處理了錯誤,不需要其他操作,反之會顯示錯誤消息;
某些元素也支持onerror; 但其處理函數沒有任何關于error信息的參數,如:
document.images[0].onerror=function(event){
console.log(event); // Event
console.log(event.type); // error
}
這里的event參數是一個類型為Event事件對象,其存儲的信息除了type返回了error,并沒有其他和錯誤相關的信息;
全局錯誤處理window.onerror通常不能恢復腳本繼續執行,但會給開發者發送錯誤信息;
window.onerror=function(error){
console.log(error);
}
show();
console.log("中止,不會被執行");
window.onload=function(){
console.log("也不會被執行");
}
可以是簡單的打印,也可以把錯誤保存到日志記錄里;
window.onerror就是綁定在window對象的error事件,也可以使用標準的添加事件偵聽的方式window.addEventListener(eventtype, handler),其需要兩個參數,eventtype為事件類型,在此為error,handler是事件處理函數,其需要一個參數event,一個ErrorEvent類型的對象,其保存著有關事件和錯誤的所有信息,如:
window.addEventListener("error", function(event){
console.log(event); // ErrorEvent
console.log(event.error); // Error對象
console.log(event.error.name);
console.log(event.error.message);
console.log(event.error.stack);
console.log(event.lineno); // 行
console.log(event.colno); // 列
console.log(event.filename);
});
在實際的開發中,這兩種方式都會被使用,只不過addEventListener有定的兼容必問題,所以要兼顧所有的瀏覽器且不太關注事件對象本身的話,就使用window.onerror;
當加載自不同域的腳本中發生語法錯誤時,為避免信息泄露,語法錯誤的細節將不會報告,只會返回簡單的"Script error.";
<script>
window.onerror=function(msg, url, lineNo, columnNo, error){
console.log(msg); // Script error
console.log(url); // ""
console.log(lineNo); // 0
console.log(columnNo); // 0
console.log(error); // null
}
</script>
<script src="https://www.zeronetwork.cn/demo/demo.js"></script>
可以針對同域和不同域的錯誤分開處理,如:
<script>
window.onerror=function(msg, url, lineNo, columnNo, error){
var str_error=msg.toLowerCase();
var sub_string="script error";
if(str_error.indexOf(sub_string) > -1)
alert("腳本發生錯誤,詳情請在控制臺查看");
else{
var message=[
'消息:' + msg,
'URL:' + url,
'行:' + lineNo,
'列:' + columnNo,
'錯誤對象:' + error
].join(" - ");
alert(message);
}
}
show();
</script>
<script src="https://www.zeronetwork.cn/demo/demo.js"></script>
從上在的執行結果來看,error事件執行了兩次,原因是使用了兩個script,也就是當一個script有錯誤發生時,它只會阻止當前的script塊,而不會阻止其他的script塊;如:
<script>
show(); // 會捕獲
console.log("不會被執行");
myshow(); // 不會捕獲
</script>
<script src="https://www.zeronetwork.cn/demo/demo.js"></script>
<script>
console.log("執行了");
demo(); // 會捕獲
console.log("不會被執行");
</script>
body元素的onerror特性,也可以充當事件處理函數,如:
<body onerror="alert('出現了錯誤');return true;">
注意,先注釋掉window.onerror等代碼;
此時,可以直接使用event、source、lineno、colno、error等屬性;
<body onerror="alert(event + '\n' + source + '\n' + lineno + '\n' + colno + '\n' + error);return true;">
當然了,也可以為body綁定error事件,此時各屬性,必須明確指定,如:
document.body.onerror=function(msg, url,lineno,colno,error){
alert(msg + '\n' + url + '\n' + lineno + '\n' + colno + '\n' + error);
return true;
}
try-catch語句:
try語句中為期待正常執行的代碼塊,當在try語句中發生錯誤,其余代碼會中止執行,catch語句就處理該錯誤,如果沒有錯誤,就跳過catch語句;try和catch必須成對出現;
try{
//code
[break]
}catch([exception]){
//code
}[finally]{
//code
}
// 如
try {
show();
alert("不能執行");
} catch (error) {
alert("出現一個錯誤:" + error);
} finally{
alert("管你呢");
}
try語句塊內的錯誤只會中止try語句塊中發生錯誤之后的邏輯代碼,并不會中止整個腳本的運行;執行try-catch語句,必須是運行時,運行時錯誤,也被稱為異常;try-catch語句中指定只能有一個catch子句;try-catch語句適合處理無法預知、無法控制的錯誤;finally常被用于無論結果是否有異常,都要執行的代碼,如:
try{
alert("try");
show();
alert("no exec");
}catch(error){
alert("catch");
}finally{
alert("finally");
}
alert("continute");
代碼執行的兩條路徑:如果沒有異常,執行路徑為:try->finally,反之為:try的部分->catch->finally;
一般用于關閉打開的鏈接和釋放資源;
var connection={open: function(){},close: function(){},send: function(data){}}
// var data="大師哥王唯"; // 注釋這一行,讓它產生異常
connection.open();
try{
connection.send(data);
}catch(exception){
console.log("出現一個錯誤");
}finally{
connection.close();
console.log("關閉了");
}
還有一個典型的應用,讀寫文件,如:
function openFile(){};
function writeFile(data){};
function closeFile(){};
openFile();
try{
writeFile();
}catch(error){
console.log(error);
}finally{
closeFile();
}
在try-catch-finally語句塊中的變量是全局變量:
try{
var name="王唯";
show();
var city="蚌埠";
}catch(error){
var age=18;
console.log(name);
}finally{
var sex="男";
console.log(name);
console.log(age);
}
console.log(name);
console.log(city);
console.log(age);
console.log(sex);
try-catch-finally與return:
如果直接在try-catch-finally語句塊中執行return,會拋出異常,如:
try {
console.log("try");
// return; // Illegal return statement 非法返回語句
console.log("try agin");
} catch (error) {
console.log(error);
// return; // Illegal return statement
}finally{
console.log("finally");
// return; // Illegal return statement
}
如:
function foo(){
try {
console.log("try");
return 1;
show();
console.log("try agin");
} catch (error) {
console.log(error);
return 2;
}finally{
console.log("finally");
return 3;
}
}
console.log(foo()); // 3
try-finally:
沒有catch從句,只有try-finally也可以,目的是,只確保執行開始和最終的過程,而不處理錯誤,如:
try{
console.log("try");
show();
}finally{
console.log("finally"); // 會執行
}
console.log("over"); // 不會執行,已中止
但此時,還是會拋出異常的,但此時,會在執行完finally后中止執行,并會查找外部的catch語句;
嵌套try-catch語句:
在catch子句中,也有可能會發生錯誤,所以就可以使用嵌套的try-catch語句,如:
try {
show();
console.log("不能執行");
} catch (error) {
console.log("出現一個錯誤:" + error);
try {
var arr=new Array(10000000000000000);
arr.push(error);
} catch (error) {
console.log("又出現了一個錯誤:" + error);
}
} finally{
console.log("管你呢");
}
也可以在try中嵌套try-catch-finally語句,如:
try{
try{
console.log("try");
show();
}catch(error){
console.log("error");
}finally{
console.log("finally");
}
}catch(error){
console.log(error);
}
一個比較典型的應用,就是處理json數據,如:
// var json='{"name":"wangwei", "age": 18, "sex": "男"}';
var json='{bad json}'; // Uncaught SyntaxError
var data=JSON.parse(json);
console.log(data.name);
console.log(data.age);
console.log(data.sex);
一量json數據發生錯誤,整個應用都會崩潰,所以應該使用try-catch,如:
<div id="msg">您的信息:</div>
<script>
window.onload=function(){
var msg=document.getElementById("msg");
try{
// var json='{"name":"王唯", "age": 18, "sex": "男"}';
var json='{bad json}'; // Uncaught SyntaxError
var data=JSON.parse(json);
msg.innerHTML +="姓名:" + data.name + ",年齡:" + data.age + ",性別:" + data.sex;
}catch(error){
msg.innerHTML="開小差了,找不到你的信息";
}
}
</script>
當使用了try-catch語句,就不會將錯誤提交給瀏覽器,也就不會觸發error事件,如:
window.onerror=function(error){
console.log(error); // 不會觸發
}
try{
show();
}catch(error){
console.log(error);
}
Error錯誤對象:
在catch中會捕獲一個Error錯誤對象;該對象在Javascript解析或運行時,一旦發生錯誤,引擎就會拋出這個對象;如果沒有相關聯的try-catch捕獲該對象,就由瀏覽器輸出這個對象;
// ...
console.log("錯誤:" + error + " name:" + error.name + " message:" + error.message);
也可以通過Error的構造器創建一個錯誤對象,這個Error對象也可用于用戶自定義的異常;語法:new Error([message[, filename[, lineNumber]]]);
實例化Error對象,也可以不使用new關鍵字,如:
var error=new Error("自定義錯誤對象");
var error=Error("不使用new");
console.log(error);
console.log(error.name); // Error
console.log(error.message); // 自定義錯誤對象
Error錯誤對象屬性:
Error類還有6個子類,其可以通過錯誤對象的name屬性返回具體異常類的名稱:
// EvalError
try{
throw new EvalError("Eval異常");
}catch(error){
console.log(error);
console.log(error instanceof EvalError); // true
console.log(error.name); // EvalError
console.log(error.message); // Eval異常
}
// RangeError
var num=1;
try{
num.toPrecision(500); // [pr??s??n] 精度
}catch(error){
console.log(error);
}
// ReferenceError
var x;
try {
x=y + 1;
} catch (error) {
console.log(error);
}
// SyntaxError
try{
eval("alert('wangwei)");
}catch(error){
console.log(error);
}
// TypeError
var num=1;
try{
num.toUpperCase(); // 無法將數字轉為大寫
}catch(error){
console.log(error);
}
// URIError (malformed [?m?l?f??md]格式不正確,畸形的)
try{
decodeURI("%%%"); // 使用了非法字符
}catch(error){
console.log(error);
}
Error對象的message屬性是瀏覽器生成的用于表示錯誤描述的信息,因為這個屬性是特定于瀏覽器的,所以不同的瀏覽器上可能產生不同的錯誤信息,如:
try {
eval("a ++ b");
show();
console.log("執行了嗎?");
} catch (error) {
// SyntaxError:Unexpected identifier或
// SyntaxError:unexpected token: identifier
console.log(error.name + ":" + error.message);
}
使用name屬性判斷錯誤類型:
try {
eval("a++b");
} catch (error) {
console.log(error instanceof SyntaxError); // true
if(error.name=="SyntaxError")
console.log(error.name + ":" + error.message);
else
console.log("未知錯誤:" + error.message);
}
拋出異常:
throw語句的作用是手動中斷程序執行,拋出一個錯誤,一般用于有目的的拋出異常,也就是允許開發者可以創建自定義錯誤;
throw可以拋出任何類型的值,也就是說,它的參數可以是任何值;語法:throw error_object;error_object可以是字符串、數字、布爾或對象;
throw "出現一個錯誤";
throw 50666;
throw true;
throw new Object();
throw {
toString:function(){
return 'Error!';
}
}
function getRectArea(width, height){
if(isNaN(width) || isNaN(height))
throw '參數應該是number類型';
return width * height;
}
getRectArea("wangwei",10);
對于Javascript引擎來說,只要遇到throw語句,程序就會終止;
也可以拋出一個Error錯誤對象;Error對象的構造函數只有一個參數,
throw new Error("請再次嘗試");
其他Error子類對象也可以拋出:
throw new SyntaxError("...");
throw new TypeError("...");
throw new RangeError("...");
throw new EvalError("...");
throw new URIError("...");
throw new ReferenceError("...");
對于Error類和其子類來說,錯誤對象的name就是其構造函數的名稱,message是其構造函數的參數;
當拋出異常后,throw之后的語句將不會執行,并跳到相關聯的catch語句中進行處理,如:
<h1>請輸入18-99之間的數字</h1>
<input id="txtInput" type="text" />
<button id="btn">確定</button>
<p id="msg"></p>
<script>
function myFun(){
var msg,x;
msg=document.getElementById("msg");
msg.innerHTML="";
x=document.getElementById("txtInput").value;
try{
if(x=="") throw "空的";
if(isNaN(x)) throw "不是數字";
x=Number(x);
if(x < 18) throw "太小";
if(x > 99) throw "太大";
msg.innerHTML="輸入的值正確:" + String(x);
}
catch(error){
msg.innerHTML="輸入的值不正確:" + error;
}
}
var btn=document.getElementById("btn");
btn.onclick=myFun;
</script>
也可以在某個語句塊的外部捕獲throw異常,如:
function sum(a,b){
if(arguments.length < 2)
throw new Error("需要兩個參數");
else
return a + b;
}
try{
console.log(sum(18));
}catch(error){
// Error:需要兩個參數
console.log(error.name + ":" + error.message);
}
可以通過instanceof判斷異常的類型來特定處理某一類的異常,例如可以區分瀏覽器拋出的異常和開發人員拋出的異常,如:
function sum(a,b){
if(arguments.length < 2)
throw new Error("需要兩個參數");
if(isNaN(a) || isNaN(b))
throw "參數是不是Number類型";
return a + b;
}
try{
console.log(sum(18,12));
}catch(error){
if(error instanceof SyntaxError)
console.log("語法錯誤:" + error.name + ":" + error.message);
else if(error instanceof Error)
console.log(error.name + ":" + error.message);
else
console.log(error);
}
注:判斷Error類型要放到if的最后一個條件;
即使在catch語句中,還可以根據實際情況,再次拋出異常,此時,其可以被外部的try-catch語句塊捕獲(如果存在的話);
當發生異常時,代碼會立即停止,僅當有try-catch語句捕獲到異常時,代碼才會繼續執行;其背后運行的原理是,當發生異常,JavaScript解釋器會立即停止執行的邏輯,并跳轉到就近的try-catch異常處理程序,如果發生異常的代碼塊中沒有相關聯的catch從句,解釋器會檢查更高層的閉合代碼塊,看它是否有相關聯的異常處理程序,以此類推,直到找到一個異常處理程序為止;如果發生異常的函數中沒有處理它的try-catch語句,異常將向上傳播到調用該函數的代碼,如此,異常就會沿著Javascript的語法結構或調用棧向上傳播;如果沒有找到任何異常處理程序,JavaScript將把異常當成程序錯誤來處理,并通過瀏覽器報告給用戶;
自定義錯誤類型:
可以基于Error類來創建自定義的錯誤類型,此時可以使用throw拋出自定義的異常類,或通過instanceof來檢查這個異常類的類型,新類型需要實現name和message屬性;
function CustomError(message){
this.name="CustomError";
this.message=message || 'Default Message';
this.stack=(new Error()).stack;
}
// CustomError.prototype=new Error();
// 或者
CustomError.prototype=Object.create(Error.prototype);
CustomError.prototype.constructor=CustomError;
try{
var name="jingjing";
if(name !=="wangwei")
throw new CustomError("自定義的錯誤類型");
}catch(error){
console.log(error.message);
}
小示例:
function UserException(message){
this.name="UserException";
this.message=message;
}
function getMothName(m){
m=m - 1; // 調整月份數字到數組索引(1=Jan,12=Dec)
var months=['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
if(months[m] !=undefined)
return months[m];
else
throw new UserException("Invalid Month No");
}
try{
var myMonth=15;
var monthName=getMothName(myMonth);
}catch(error){
var monthName="未知";
console.log(error.name + ":" + error.message);
}
小示例:驗證電話號碼,如:
function Telephone(num){
num=String(num);
var pattern=/^((0\d{2,3}-\d{7,8})|(1[3584]\d{9}))$/;
if(pattern.test(num)){
this.value=num.match(pattern)[0];
this.valueOf=function(){
return this.value;
};
this.toString=function(){
return String(this.value);
}
}else{
throw new TelephoneFormatException(num);
}
}
function TelephoneFormatException(value){
this.name="TelephoneFormatException";
this.message="電話號碼格式不正確";
this.value=value;
this.toString=function(){
return this.value + ":" + this.message;
}
}
// 應用
var TELEPHONE_INVALID=-1;
var TELEPHONE_UNKNOWN_ERROR=-2;
function verifyTelephone(num){
try{
num=new Telephone(num);
}catch(error){
if(error instanceof TelephoneFormatException)
return TELEPHONE_INVALID;
else
return TELEPHONE_UNKNOWN_ERROR;
}
return num.toString();
}
console.log(verifyTelephone("010-66668888"));
console.log(verifyTelephone("13812345678"));
console.log(verifyTelephone("138123456")); // -1
console.log(verifyTelephone("wangwei")); // -1
常見錯誤:
由于javaScript是松散類型的,也不會驗證函數的參數,因此錯誤只會在運行時出現;一般來說,需要關注三種錯誤:類型轉換錯誤、數據類型錯誤、通信錯誤;
類型轉換錯誤:
一般發生在使用某個操作符,或者使用其他可能自動轉換值的數據類型的語言結構時;
function output(str1,str2,str3){
var result=str1 + str2;
if(str3)
result +=str3;
return result;
}
console.log(output(1,2,3));
console.log(output(1,2));
console.log(output(1,2,0));
console.log(output(1,2,"wangwei"));
這就是一個非常典型的與期望不一致的方式;
數據類型錯誤:
在流控制語句中使用非布爾值,是極為常見的一個錯誤來源,為避免此類錯誤,就要做到在條件比較時確定傳入的是布爾值,例如,把if語句改成:if(typeof str3=='number');
所以在使用某個變量或對象時,一定要適當地檢查它的數據類型,如:
function reverseSort(values){
// if(values){
// values.sort();
// values.reverse();
// }
if(arguments.length > 0){
if(!Array.isArray(values))
return [];
else{
values.sort();
values.reverse();
return values;
}
}
return [];
}
var arr=[3,2,6,9,4];
// var arr=100; // Uncaught TypeError: values.sort is not a function
console.log(reverseSort(arr));
另一個常見的錯誤就是將參數與null值進行比較。與null進行比較只能確保相應的值不是null和undefined。要確保傳入的值有效,僅檢測null值是不夠的;
function reverseSort(values){
// if(values !=null){ // 任何非數組值都會導致錯誤
if(values instanceof Array) // 非數組值被忽略
values.sort();
values.reverse();
}
return values;
}
var arr=[3,2,6,9,4];
// var arr=100; // Uncaught TypeError: values.sort is not a function
console.log(reverseSort(arr));
// 或
function reverseSort(values, fun){
if(values instanceof Array){
if(fun !=null && typeof fun==="function")
values.sort(fun);
else
values.sort();
}
return values;
}
var arr=[3,2,6,9,4];
console.log(reverseSort(arr, function(a,b){
return a > b ? -1 : 1;
}));
通信錯誤:最典型的就是Ajax應用,用其可以動態加載信息,但是,javascript與服務器之間的任何一次通信,都有可能會產生錯誤;
調試技巧:
使用警告框: 這是最簡單、流行的方式,如:
function test(){
alert("函數內");
var iNum1=5, iNum2=10;
alert(iNum1);
var iResult=iNum1 + iNum2;
alert(iResult);
}
test();
拋出自定義錯誤:
function assert(bCondition, sErrorMessage){
if(!bCondition)
throw new Error(sErrorMessage);
}
function divide(iNum1, iNum2){
assert(arguments.length==2, "divide需要兩個參數");
assert((!isNaN(iNum1) && !isNaN(iNum2)), "需要Number類型");
return iNum1 / iNum2;
}
console.log(divide(10,2));
console.log(divide(10,"c")); // 異常
// 或
try{
console.log(divide(10,"c"));
}catch(error){
console.log(error.name + ":" + error.message);
}
Javascript校驗器:
jslint的主要目的是指出不合規范的js語法和可能的語法錯誤,包括一些不良代碼;官網:http://www.jslint.com/
如以下,會給出警告:
調試器:
Javascript自身不具備調試器,但目前所有的瀏覽器可以使用自身的調試器;
IE調試:
啟用IE的調試功能:
菜單“工具”|“Internet選項”命令,打開“Internet選項”對話框,在“高級”選項卡中,找到兩個“禁用腳本調試”復選框并取消;開始調試,調試的主要工作是反復地跟蹤代碼,找出錯誤并修正;
設置斷點:
在調試程序窗口中,將光標移動到需要添加斷點的行上,按一次F9鍵或單擊,當前行的背景色變為紅色,并且在窗口左邊界上標上紅色的圓點,當程序運行到斷點時就會暫停;
運行調試:
單擊繼續或按F5進行逐步運行調試;F10步進、F11步入,都可以繼續向下執行;將鼠標移動到變量名上時,會顯示變量當前時刻的值;或者在右側的“監視”窗格中可以觀察該變量的值;點擊變量信息框中的變量值或右側“監視”空格中的變量值可以修改變量的當前值;更多的調試操作:查看調用關系、監視特定變量的值等;
// 示例
var balance=200.0; //
var willPay=20.0;
function pay(_balance, _pay){
return _balance - _pay;
}
function showBalance(){
debugger;
var blnc=pay(balance,willPay);
alert("當前余額:" + blnc);
}
showBalance();
日志輸出:
程序運行時,有些中間數據需要記錄,以便檢查程序運行的狀態;對于JavaScript記錄中間數據通常是以日志的形式記錄需要記錄的數據,再發送到服務器上保存起來;日志記錄的內容可以是任意的信息,根據開發者的需要而定;
*請認真填寫需求信息,我們會在24小時內與您取得聯系。