# 2021/07 JavaScript 例外處理 ###### tags: `讀書會` 'JavaScript` * ## **例外處理 (error handling)** 是 JavaScript 的一種程式流程控制,你可以在程式執行可能拋出 **"錯誤"** 的地方使用,主動捕捉並處理錯誤,避免整個程式因為發生錯誤而停止執行。 ## **例外處理陳述式** * try:放入要測試的程式碼 * catch:處理例外的程式碼,如果有錯誤,則錯誤訊息會被傳到這個區塊 ,並且執行這個區塊的行為 * finally: 結束執行的程式碼,不論有無錯誤最後這個區塊都會被執行 * throw:拋出錯誤訊息 ## 使用throw的時機 Error.prototype 物件包含如下屬性: constructor–指向例項的建構函式 message–錯誤資訊 name–錯誤的名字(型別) ```javascript= console.log(Error.prototype) ``` ```javascript= class Account { constructor(name, number , balance){ this.name = name; this.number = number; this.balance = balance; } withdraw(money){ if (money> this.balance){ // console.log('Insufficient Balance'); throw new Error("Insufficient Balance (throw)"); } //客製化一個自己的錯誤物件 - new Error(): this.balance -= money; } deposit(money){ if (money<0){ // console.log("Insufficient deposit"); throw new Error("Insufficient deposit (throw)"); } //客製化一個自己的錯誤物件 - new Error(): else{ this.balance +=money; } } toString() { return `(${this.name}, ${this.number}, ${this.balance})`; } } let kevinacc = new Account('kevin',001,10000); console.log(kevinacc.toString()); // kevinacc.withdraw(1000000); kevinacc.deposit(-1000); ``` ## 使用try-catch-finally例子 * 當try catch 區塊結束 * 跳出視窗 "Error : Insufficient Balance (throw)" * Catch內有兩個錯誤物件(Error Object) * name = 錯誤類型 "Error" * message= 文字說明 "Insufficient Balance (throw)" ```javascript= // 使用例子class Account() let kevinacc = new Account('kevin',001,10000); try { kevinacc.withdraw(1000000) } catch(e){ console.log(e.name + " : " +e.message); } finally{ console.log(`Account information:${kevinacc.toString()}`); } ``` ## try-catch 並不支援多的Catch區塊 ```javascript= try { doFoo(); } catch (err) { if (err instanceof TypeError) { console.log('TypeError'); } else if (err instanceof ReferenceError) { console.log('ReferenceError'); } } ``` *** ## 下面是一些常見錯誤的種類。 | 錯誤類型 | 說明 | | -------------- |:--------------------------------------------------------- | RangeError | 一個數值超過允許範圍 | | ReferenceError | 存取未宣告的變數 | | SyntaxError | 程式語法錯誤 | | TypeError | 型別錯誤 | * ## 可透過程式編譯器避免 *尚未宣告變數,或是輸入錯誤產生,此類錯誤通常會被程式編輯器提醒。* ### ReferenceError ```javascript= const PI = 3.1415926 console.log(pi) // ReferenceError: pi is not defined ``` ### SyntaxError ```javascript= function course(name){ this.name console.log('I am taking '+ name + " class right now.";) //;的位置 } course("PE") //Uncaught SyntaxError: missing ) after argument list ``` ```javascript= // SyntaxError try{ Let x=1000; //L應該小寫 let } catch(err){ console.log(`${err.name}": ${err.message}`); } ``` * ## 使用例外處理方法try-catch ### RangeError ```javascript= var MIN = 300; var MAX = 600; var check = function(num) { if (num < MIN || num > MAX) { throw new RangeError('Parameter must be between ' + MIN + ' and ' + MAX); } }; try { check(5000); } catch (e) { if (e instanceof RangeError) console.log(`${e.name}:${e.message}`); } ``` ### TypeError ```javascript= let num = 1202; try { num.toUpperCase(); } catch (err) { console.log(err.message) console.log(err.stack) console.log(err.name) } // num.toUpperCase is not a function // TypeError: num.toUpperCase is not a function // TypeError ``` ![](https://i.imgur.com/zm4Jaze.png) ## 自訂錯誤型態 對於特定的要求與錯誤繼承 ```javascript= class illegalArgumentError extends Error { //1. 繼承Error constructor(message) { super(message); // 2.呼叫Error建構式 } get name() { return illegalArgumentError.name; //3.錯誤型態名稱 } } class InsufficientException extends Error { constructor(message, balance) { super(message); this.balance = balance; } get name() { return InsufficientException.name; } } class Account { constructor(name, number, balance) { this.name = name; this.number = number; this.balance = balance; } withdraw(money) { if (money < 0) { //引數不正確 throw new illegalArgumentError('提款金額不得為負'); } if (money > this.balance) { //餘額不正確 throw new InsufficientException('餘額不足',this.balance); } this.balance -= money; } deposit(money) { if (money < 0) { //引述不正確 throw new illegalArgumentError('存款金額不得為負'); } this.balance += money; } toString() { return `(${this.name}, ${this.number}, ${this.balance})`; } } let acct = new Account('kevin', '123-788', 1000); try { acct.withdraw(5000); } catch (err) { if (err instanceof InsufficientException) { //餘額不足的處理 console.log(`${err.name}:${err.message}`); console.log('目前的餘額balance: ', err.balance); } else { throw err; //再度出現錯誤 } } ``` ## 非同步任務處理錯誤 - Promise ## 追蹤堆疊 首先(函式的)呼叫棧的原理 為後進先出 LIFO (last in, first out) ```javascript= function c() { console.log('c'); console.trace(); } function b() { console.log('b'); c(); } function a() { console.log('a'); b(); } a(); ``` ![](https://i.imgur.com/yPlJ7xw.png) ```javascript= function c() { console.log('c'); } function b() { console.log('b'); c(); console.trace(); } function a() { console.log('a'); b(); } a(); ``` ![](https://i.imgur.com/rIk1BpL.png) ```javascript= // 選擇性敘述的練習-season // 輸入月份1~12月,利用switch判斷相對應的季節春、夏、秋、冬並印出。若不在此範圍則印出”輸入錯誤”。 function season(month) { this.month = month; switch (this.month) { case 1: case 2: case 12: document.write("冬天"); break; case 3: case 4: case 5: document.write("春天"); break; case 6: case 7: case 8: document.write("夏天"); break; case 9: case 10: case 11: document.write("冬天"); break; default: document.write("輸入錯誤月份"); throw new Error("輸入錯誤月份"); } } season(5); document.write("<br>"); season(12); document.write("<br>"); // 測試函數也可以指定給一個變數 try { var seasontest = season; seasontest(13); } catch (err) { console.log(err.message) console.log(err.stack) console.log(err.name) } ``` ## 參考網站 [JavaScript錯誤處理和堆疊追蹤詳解 | 程式前沿](https://codertw.com/%E5%89%8D%E7%AB%AF%E9%96%8B%E7%99%BC/240477/) [錯誤處理](https://www.slideshare.net/JustinSDK/7-194523942) [Fooish 程式技術](https://www.fooish.com/javascript/error-handling-try-catch-finally.html#:~:text=JavaScript%20try%20catch%20finally%20Error%20Handling%20%28%E4%BE%8B%E5%A4%96%E8%99%95%E7%90%86%29%20%E4%BE%8B%E5%A4%96%E8%99%95%E7%90%86,try%20%E5%8D%80%E5%A1%8A%E4%B8%AD%EF%BC%9B%E7%84%B6%E5%BE%8C%E5%87%BA%E9%8C%AF%E6%99%82%E7%9A%84%E8%99%95%E7%90%86%E7%A8%8B%E5%BC%8F%E7%A2%BC%E6%94%BE%E5%9C%A8%20catch%20%E5%8D%80%E5%A1%8A%E4%B8%AD%EF%BC%9B%E8%80%8C%E6%94%BE%E5%9C%A8%20finally%20%E5%8D%80%E5%A1%8A%E4%B8%AD%E7%9A%84%E7%A8%8B%E5%BC%8F%E7%A2%BC%E7%84%A1%E8%AB%96%E5%A6%82%E4%BD%95%E9%83%BD%E6%9C%83%E5%9C%A8%E6%9C%80%E5%BE%8C%E8%A2%AB%E5%9F%B7%E8%A1%8C%E3%80%82.%20try-catch-finally%20%E7%94%A8%E6%B3%95%E4%BE%8B%E5%AD%90%EF%BC%9A.) [MDN 流程控制與例外處理](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Guide/Control_flow_and_error_handling) [璇之又璇的網路世界](https://shawnlin0201.github.io/JavaScript/JavaScript-Exception-Handling/) [卡斯博's blog](https://wcc723.github.io/development/2020/09/16/chrome-js-alert/) [非同步](https://ithelp.ithome.com.tw/articles/10221800) [IT邦幫忙 - [Day04] JavaScript - 程式控制結構](https://ithelp.ithome.com.tw/articles/10217417) https://codertw.com/%E5%89%8D%E7%AB%AF%E9%96%8B%E7%99%BC/240477/