全面瞭解javascript中的錯誤處理機制_javascript技巧

來源:互聯網
上載者:User

前面的話

錯誤處理對於web應用程式開發至關重要,不能提前預測到可能發生的錯誤,不能提前採取恢複策略,可能導致較差的使用者體驗。由於任何javascript錯誤都可能導致網頁無法使用,因此作為開發人員,必須要知道何時可能出錯,為什麼會出錯,以及會出什麼錯。本文將詳細介紹javascript中的錯誤處理機制

error對象  

error對象是包含錯誤資訊的對象,是javascript的原生對象。當代碼解析或運行時發生錯誤,javascript引擎就會自動產生並拋出一個error對象的執行個體,然後整個程式就中斷在發生錯誤的地方

console.log(t);//Uncaught ReferenceError: t is not defined

ECMA-262規定了error對象包括兩個屬性:message和name。message屬性儲存著錯誤資訊,而name屬性儲存錯誤類型

//一般地,使用try-catch語句來捕獲錯誤try{  t;}catch(ex){  console.log(ex.message);//t is not defined   console.log(ex.name);//ReferenceError}

瀏覽器還對error對象的屬性做了擴充,添加了其他相關資訊。其中各瀏覽器廠商實現最多的是stack屬性,它表示棧跟蹤資訊(safari不支援)

try{  t;}catch(ex){  console.log(ex.stack);//@file:///D:/wamp/www/form.html:12:2} 

當然,可以使用error()建構函式來建立錯誤對象。如果指定message參數,則該error對象將把它用做它的message屬性;若不指定,它將使用一個預定義的預設字串作為該屬性的值

new Error();new Error(message);  //一般地,使用throw語句來拋出錯誤throw new Error('test');//Uncaught Error: testthrow new Error();//Uncaught Error
function UserError(message) {  this.message = message;  this.name = "UserError";}UserError.prototype = new Error();UserError.prototype.constructor = UserError;throw new UserError("errorMessage");//Uncaught UserError: errorMessage

當不使用new操作符,直接將Error()建構函式像一個函數一樣調用時,它的行為和帶new操作符調用時一樣

Error();Error(message);  throw Error('test');//Uncaught Error: testthrow Error();//Uncaught Error

error對象有一個toString()方法,返回'Error:'+ error對象的message屬性

var test = new Error('testError');console.log(test.toString());//'Error: testError'

error類型  

執行代碼期間可能會發生的錯誤有多種類型。每種錯誤都有對應的錯誤類型,而當錯誤發生時,就會拋出相應類型的錯誤對象。ECMA-262定義了下列7種錯誤類型:

ErrorEvalError(eval錯誤)RangeError(範圍錯誤)ReferenceError(引用錯誤)SyntaxError(語法錯誤)TypeError(類型錯誤)URIError(URI錯誤)

其中,Error是基底類型,其他錯誤類型都繼承自該類型。因此,所有錯誤類型共用了一組相同的屬性。Error類型的錯誤很少見,如果有也是瀏覽器拋出的;這個基底類型的主要目的是供開發人員拋出自訂錯誤

【EvalError(eval錯誤)】

eval函數沒有被正確執行時,會拋出EvalError錯誤。該錯誤類型已經不再在ES5中出現了,只是為了保證與以前代碼相容,才繼續保留

【RangeError(範圍錯誤)】

RangeError類型的錯誤會在一個值超出相應範圍時觸發,主要包括超出數組長度範圍以及超出數字取值範圍等

new Array(-1);//Uncaught RangeError: Invalid array lengthnew Array(Number.MAX_VALUE);//Uncaught RangeError: Invalid array length(1234).toExponential(21);//Uncaught RangeError: toExponential() argument must be between 0 and 20(1234).toExponential(-1);////Uncaught RangeError: toExponential() argument must be between 0 and 20

【ReferenceError(引用錯誤)】

引用一個不存在的變數或左值(lvalue)類型錯誤時,會觸發ReferenceError(引用錯誤)

a;//Uncaught ReferenceError: a is not defined1++;//Uncaught ReferenceError: Invalid left-hand side expression in postfix operation

【SyntaxError(語法錯誤)】

當不符合文法規則時,會拋出SyntaxError(語法錯誤)

//變數名錯誤var 1a;//Uncaught SyntaxError: Unexpected number// 缺少括弧console.log 'hello');//Uncaught SyntaxError: Unexpected string

【TypeError(類型錯誤)】

在變數中儲存著意外的類型時,或者在訪問不存在的方法時,都會導致TypeError類型錯誤。錯誤的原因雖然多種多樣,但歸根結底還是由於在執行特定類型的操作時,變數的類型並不符合要求所致

var o = new 10;//Uncaught TypeError: 10 is not a constructoralert('name' in true);//Uncaught TypeError: Cannot use 'in' operator to search for 'name' in trueFunction.prototype.toString.call('name');//Uncaught TypeError: Function.prototype.toString is not generic

【URIError(URI錯誤)】

URIError是URI相關函數的參數不正確時拋出的錯誤,主要涉及encodeURI()、decodeURI()、encodeURIComponent()、decodeURIComponent()、escape()和unescape()這六個函數

decodeURI('%2');// URIError: URI malformed

error事件  

任何沒有通過try-catch處理的錯誤都會觸發window對象的error事件

error事件可以接收三個參數:錯誤訊息、錯誤所在的URL和行號。多數情況下,只有錯誤訊息有用,因為URL只是給出了文檔的位置,而行號所指的程式碼既可能出自嵌入的JavaScript代碼,也可能出自外部的檔案

要指定onerror事件處理常式,可以使用DOM0級技術,也可以使用DOM2級事件的標準格式

//DOM0級window.onerror = function(message,url,line){  alert(message);}//DOM2級window.addEventListener("error",function(message,url,line){  alert(message);});

瀏覽器是否顯示標準的錯誤訊息,取決於onerror的傳回值。如果傳回值為false,則在控制台中顯示錯誤訊息;如果傳回值為true,則不顯示

//控制台顯示錯誤訊息window.onerror = function(message,url,line){  alert(message);  return false;}a;//控制台不顯示錯誤訊息window.onerror = function(message,url,line){  alert(message);  return true;}a;

這個事件處理常式是避免瀏覽器報告錯誤的最後一道防線。理想情況下,只要可能就不應該使用它。只要能夠適當地使用try-catch語句,就不會有錯誤交給瀏覽器,也就不會觸發error事件

映像也支援error事件。只要映像的src特性中的URL不能返回可以被識別的映像格式,就會觸發error事件。此時的error事件遵循DOM格式,會返回一個以映像為目標的event對象

載入映像失敗時會顯示一個警告框。發生error事件時,映像下載過程已經結束,也就是不能再重新下載了

var image = new Image();image.src = 'smilex.gif';image.onerror = function(e){  console.log(e);}

throw語句與拋出錯誤  

throw語句用於拋出錯誤。拋出錯誤時,必須要給throw語句指定一個值,這個值是什麼類型,沒有要求

[注意]拋出錯誤的過程是阻塞的,後續代碼將不會執行

throw 12345;throw 'hello world';throw true;throw {name: 'javascript'};

可以使用throw語句手動拋出一個Error對象

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來建立自訂錯誤類型(原型鏈在第6章中介紹)。此時,需要為新建立的錯誤類型指定name和message屬性

瀏覽器對待繼承自Error的自訂錯誤類型,就像對待其他錯誤類型一樣。如果要捕獲自己拋出的錯誤並且把它與瀏覽器錯誤區別對待的話,建立自訂錯誤是很有用的

function CustomError(message){  this.name = 'CustomError';  this.message = message;}CustomError.prototype = new Error();throw new CustomError('my message');

在遇到throw語句時,代碼會立即停止執行。僅當有try-catch語句捕獲到被拋出的值時,代碼才會繼續執行

更詳細的解釋為:當拋出異常時,javascript解譯器會立即停止當前正在執行的邏輯,並跳轉到就近的例外處理常式。例外處理常式是用try-catch語句的catch從句編寫的。如果拋出異常的代碼塊沒有一條相關聯的catch從句,解譯器會檢查更高層的閉合代碼塊,看它是否有相關聯的例外處理常式。以此類推,直到找到一個例外處理常式為止。如果拋出異常的函數沒有處理它的try-catch語句,異常將向上傳播到調用該函數的代碼。這樣的話,異常就會沿著javascript方法的詞法結構和調用棧向上傳播。如果沒有找到任何例外處理常式,javascript將把異常當成程式錯誤來處理,並報告給使用者

try catch語句與捕獲錯誤  

ECMA-262第3版引入了try-catch語句,作為JavaScript中處理異常的一種標準方式,用於捕獲和處理錯誤

其中,try從句定義了需要處理的異常所在的代碼塊。catch從句跟隨在try從句之後,當try塊內某處發生了異常時,調用catch內的代碼邏輯。catch從句後跟隨finally塊,後者中放置清理代碼,不管try塊中是否產生異常,finally塊內的邏輯總是會執行。儘管catch和finally都是可選的,但try從句需要至少二者之一與之組成完整的語句

try/catch/finally語句塊都需要使用花括弧括起來,這裡的花括弧是必需的,即使從句中只有一條語句也不能省略花括弧

try{  //通常來講,這裡的代碼會從頭到尾而不會產生任何問題  //但有時會拋出一個異常,要麼是由throw語句直接拋出,要麼通過調用一個方法間接拋出}catch(e){  //若且唯若try語句塊拋出了異常,才會執行這裡的代碼  //這裡可以通過局部變數e來獲得對Error對象或者拋出的其他值的引用  //這裡的代碼塊可以基於某種原因處理這個異常,也可以忽略這個異常,還可以通過throw語句重新拋出異常}finally{  //不管try語句是否拋出了異常,finally裡的邏輯總是會執行,終止try語句塊的方式有:  //1、正常終止,執行完語句塊的最後一條語句  //2、通過break、continue或return語句終止  //3、拋出一個異常,異常被catch從句捕獲  //4、拋出一個異常,異常未被捕獲,繼續向上傳播}

一般地,把所有可能會拋出錯誤的代碼都放在try語句塊中,而把那些用於錯誤處理的代碼放在catch塊中

如果try塊中的任何代碼發生了錯誤,就會立即結束代碼執行過程,然後接著執行catch塊。此時,catch塊會接收到一個錯誤資訊的對象,這個對象中包含的實際資訊會因瀏覽器而異,但共同的是有一個儲存著錯誤訊息的message屬性

[注意]一定要給error對象起個名字,置空會報語法錯誤

try{  q;}catch(error){  alert(error.message);//q is not defined}//Uncaught SyntaxError: Unexpected token )try{  q;}catch(){  alert(error.message);}

catch接受一個參數,表示try代碼塊拋出的值

function throwIt(exception) { try {  throw exception; } catch (e) {  console.log('Caught: '+ e); }}throwIt(3);// Caught: 3throwIt('hello');// Caught: hellothrowIt(new Error('An error happened'));// Caught: Error: An error happened

catch代碼塊捕獲錯誤之後,程式不會中斷,會按照正常流程繼續執行下去

try{ throw "出錯了";} catch (e) { console.log(111);}console.log(222);// 111// 222

為了捕捉不同類型的錯誤,catch代碼塊之中可以加入判斷語句

try { foo.bar();} catch (e) { if (e instanceof EvalError) {  console.log(e.name + ": " + e.message); } else if (e instanceof RangeError) {  console.log(e.name + ": " + e.message); } // ...}

雖然finally子句在try-catch語句中是可選的,但finally子句一經使用,其代碼無論如何都會執行。換句話說,try語句塊中的代碼全部正常執行,finally子句會執行;如果因為出錯而執行了catch語句塊,finally子句照樣還會執行。只要代碼中包含finally子句,則無論try或catch語句塊中包含什麼代碼——甚至return語句,都不會阻止finally子句的執行

//由於沒有catch語句塊,所以錯誤沒有捕獲。執行finally代碼塊以後,程式就中斷在錯誤拋出的地方function cleansUp() { try {  throw new Error('出錯了……');  console.log('此行不會執行'); } finally {  console.log('完成清理工作'); }}cleansUp();// 完成清理工作// Error: 出錯了……
function testFinnally(){  try{    return 2;  }catch(error){    return 1;  }finally{    return 0;  }}testFinnally();//0

[注意]return語句的count的值,是在finally代碼塊運行之前,就擷取完成了

var count = 0;function countUp() { try {  return count; } finally {  count++; }}countUp();// 0console.log(count);// 1
function f() { try {  console.log(0);  throw "bug"; } catch(e) {  console.log(1);  return true; // 這句原本會延遲到finally代碼塊結束再執行  console.log(2); // 不會運行 } finally {  console.log(3);  return false; // 這句會覆蓋掉前面那句return  console.log(4); // 不會運行 } console.log(5); // 不會運行}var result = f();// 0// 1// 3console.log(result);// false

【tips】塊級範圍

try-catch語句的一個常見用途是建立塊級範圍,其中聲明的變數僅僅在catch內部有效

ES6引入了let關鍵字,為其聲明的變數建立塊級範圍。但是,在目前ES3和ES5的情況下,常常使用try-catch語句來實作類別似的效果

由下面代碼可知,e僅存在於catch分句內部,當試圖從別處引用它時會拋出錯誤

try{  throw new Error();//拋出錯誤}catch(e){  console.log(e);//Error(…)}console.log(e);//Uncaught ReferenceError: e is not defined

常見錯誤錯誤處理的核心是首先要知道代碼裡會發生什麼錯誤。由於javaScript是鬆散類型的,而且也不會驗證函式的參數,因此錯誤只會在代碼期間出現。一般來說,需要關注三種錯誤:類型轉換錯誤、資料類型錯誤、通訊錯誤

【類型轉換錯誤】

類型轉換錯誤發生在使用某個操作符,或者使用其他可能自動轉換值的資料類型的語言結構時

容易發生類型轉換錯誤的地方是流量控制語句。像if之類的語句在確定下一步操作之前,會自動把任何值轉換成布爾值。尤其是if語句,如果使用不當,最容易出錯

未使用過的命名變數會自動被賦予undefined值。而undefined值可以被轉換成布爾值false,因此下面這個函數中的if語句實際上只適用於提供了第三個參數的情況。問題在於,並不是只有undefined才會被轉換成false,也不是只有字串值才可以轉換為true。例如,假設第三個參數是數值0,那麼if語句的測試就會失敗,而對數值1的測試則會通過

function concat(str1,str2,str3){  var result = str1 + str2;  if(str3){ //絕對不要這樣    result += str3;  }  return result;}

在流量控制語句中使用非布爾值,是極為常見的一個錯誤來源。為避免此類錯誤,就要做到在條件比較時切實傳入布爾值。實際上,執行某種形式的比較就可以達到這個目的

function concat(str1,str2,str3){  var result = str1 + str2;  if(typeof str3 == 'string'){ //更合適    result += str3;  }  return result;}

【資料類型錯誤】

javascript是鬆散類型的,在使用變數和函數參數之前,不會對它們進行比較以確保它們的資料類型正確。為了保證不會發生資料類型錯誤,只能編寫適當的資料類型檢測代碼。在將預料之外的值傳遞繪函數的情況下,最容易發生資料類型錯誤

//不安全的函數,任何非數組值都會導致錯誤function reverseSort(values){  if(values){    values.sort();    values.reverse();  }}

另一個常見的錯誤就是將參數與null值進行比較。與null進行比較只能確保相應的值不是null和undefined。要確保傳入的值有效,僅檢測null值是不夠的

//不安全的函數,任何非數組值都會導致錯誤function reverseSort(values){  if(values != null){    values.sort();    values.reverse();  }}

如果傳入一個包含sort()方法的對象(而不是數組)會通過檢測,但調用reverse()函數時可能會出錯

//不安全的函數,任何非數組值都會導致錯誤function reverseSort(values){  if(typeof values.sort == 'function'){    values.sort();    values.reverse();  }}

在確切知道應該傳入什麼類型的情況下,最好是使用instanceof來檢測其資料類型

//安全,非數組值被忽略function reverseSort(values){  if(values instanceof Array){    values.sort();    values.reverse();  }}

【通訊錯誤】

隨著Ajax編程的興起,Web應用程式在其生命週期內動態載入資訊或功能,已經成為一件司空見慣的事。不過,javascript與伺服器之間的任何一次通訊,都有可能會產生錯誤

最常見的問題是在將資料發送給伺服器之前,沒有使用encodeURIComponent()對資料進行編碼

//錯誤http://www.yourdomain.com/?redir=http://www.sometherdomain.com?a=b&c=d//針對'redir='後面的所有字串調用encodeURIComponent()就可以解決這個問題http://www.yourdomain.com/?redir=http:%3A%2F%2Fwww.sometherdomain.com%3Fa%3Db%26c%3Dd

以上這篇全面瞭解javascript中的錯誤處理機制就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支援雲棲社區。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.