try..catch 不能捕獲的錯誤有哪些?注意事項又有哪些?

2021-03-02 前端AE86

本文已經原作者 Ashish Lahoti  授權翻譯。

今天的內容中,我們來學習一下使用try、catch、finally和throw進行錯誤處理。我們還會講一下 JS 中內置的錯誤對象(Error, SyntaxError, ReferenceError等)以及如何定義自定義錯誤。

1.使用 try..catch..finally..throw

在 JS 中處理錯誤,我們主要使用try、catch、finally和throw關鍵字。

finally 塊是最終結果無論如何,都會執行的一個塊,可以在這個塊裡面做一些需要善後的事情

1.1 try

每個try塊必須與至少一個catch或finally塊,否則會拋出SyntaxError錯誤。

我們單獨使用try塊進行驗證:

try {
  throw new Error('Error while executing the code');
}

ⓧ Uncaught SyntaxError: Missing catch or finally after try

1.2 try..catch

建議將try與catch塊一起使用,它可以優雅地處理try塊拋出的錯誤。

try {
  throw new Error('Error while executing the code');
} catch (err) {
  console.error(err.message);
}

➤ ⓧ Error while executing the code

1.2.1  try..catch 與 無效代碼

try..catch 無法捕獲無效的 JS 代碼,例如try塊中的以下代碼在語法上是錯誤的,但它不會被catch塊捕獲。

try {
  ~!$%^&*
} catch(err) {
  console.log("這裡不會被執行");
}

➤ ⓧ Uncaught SyntaxError: Invalid or unexpected token

1.2.2  try..catch 與 異步代碼

同樣,try..catch無法捕獲在異步代碼中引發的異常,例如setTimeout:

try {
  setTimeout(function() {
    noSuchVariable;   // undefined variable
  }, 1000);
} catch (err) {
  console.log("這裡不會被執行");
}

未捕獲的ReferenceError將在1秒後引發:

➤ ⓧ Uncaught ReferenceError: noSuchVariable is not defined

所以 ,我們應該在異步代碼內部使用 try..catch 來處理錯誤:

setTimeout(function() {
  try {
    noSuchVariable;
  } catch(err) {
    console.log("error is caught here!");
  }
}, 1000);

1.2.3 嵌套 try..catch

我們還可以使用嵌套的try和catch塊向上拋出錯誤,如下所示:

try {
  try {
    throw new Error('Error while executing the inner code');
  } catch (err) {
    throw err;
  }
} catch (err) {
  console.log("Error caught by outer block:");
  console.error(err.message);
}

Error caught by outer block:
➤ ⓧ Error while executing the code

1.3  try..finally

不建議僅使用 try..finally 而沒有 catch 塊,看看下面會發生什麼:

try {
  throw new Error('Error while executing the code');
} finally {
  console.log('finally');
}

finally
➤ ⓧ Uncaught Error: Error while executing the code

這裡注意兩件事:

即使從try塊拋出錯誤後,也會執行finally塊

如果沒有catch塊,錯誤將不能被優雅地處理,從而導致未捕獲的錯誤

1.4 try..catch..finally

建議使用try...catch塊和可選的finally塊。

try {
  console.log("Start of try block");
  throw new Error('Error while executing the code');
  console.log("End of try block -- never reached");
} catch (err) {
  console.error(err.message);
} finally {
  console.log('Finally block always run');
}
console.log("Code execution outside try-catch-finally block continue..");

Start of try block
➤ ⓧ Error while executing the code
Finally block always run
Code execution outside try-catch-finally block continue..

這裡還要注意兩件事:

即使在try塊拋出錯誤之後,finally塊仍然執行

finally塊通常用於清理資源或關閉流,如下所示:

try {
  openFile(file);
  readFile(file);
} catch (err) {
  console.error(err.message);
} finally {
  closeFile(file);
}

1.5 throw

throw語句用於引發異常。

throw <expression>

// throw primitives and functions
throw "Error404";
throw 42;
throw true;
throw {toString: function() { return "I'm an object!"; } };

// throw error object
throw new Error('Error while executing the code');
throw new SyntaxError('Something is wrong with the syntax');
throw new ReferenceError('Oops..Wrong reference');

// throw custom error object
function ValidationError(message) {
  this.message = message;
  this.name = 'ValidationError';
}
throw new ValidationError('Value too high');

2. 異步代碼中的錯誤處理

對於異步代碼的錯誤處理可以Promise和async await。

2.1 Promise 中的 then..catch

我們可以使用then()和catch()連結多個 Promises,以處理鏈中單個 Promise 的錯誤,如下所示:

Promise.resolve(1)
  .then(res => {
      console.log(res);  // 列印 '1'

      throw new Error('something went wrong');  // throw error

      return Promise.resolve(2);  // 這裡不會被執行
  })
  .then(res => {
      // 這裡也不會執行,因為錯誤還沒有被處理
      console.log(res);    
  })
  .catch(err => {
      console.error(err.message);  // 列印 'something went wrong'
      return Promise.resolve(3);
  })
  .then(res => {
      console.log(res);  // 列印 '3'
  })
  .catch(err => {
      // 這裡不會被執行
      console.error(err);
  })

我們來看一個更實際的示例,其中我們使用fetch調用API,該 API 返回一個promise對象,我們使用catch塊優雅地處理 API 失敗。

function handleErrors(response) {
    if (!response.ok) {
        throw Error(response.statusText);
    }
    return response;
}

fetch("http://httpstat.us/500")
    .then(handleErrors)
    .then(response => console.log("ok"))
    .catch(error => console.log("Caught", error));

Caught Error: Internal Server Error
    at handleErrors (<anonymous>:3:15)

2.2 try..catch 和 async await

在 async await  中 使用try..catch 比較容易:

(async function() {
    try {
        await fetch("http://httpstat.us/500");
    } catch (err) {
        console.error(err.message);
    }
})();

讓我們看同一示例,其中我們使用fetch調用API,該API返回一個promise對象, 我們使用try..catch塊優雅地處理API失敗。

function handleErrors(response) {
    if (!response.ok) {
        throw Error(response.statusText);
    }
}

(async function() {
    try {
      let response = await fetch("http://httpstat.us/500");
      handleErrors(response);
      let data = await response.json();
      return data;
    } catch (error) {
        console.log("Caught", error)
    }
})();

Caught Error: Internal Server Error
    at handleErrors (<anonymous>:3:15)
    at <anonymous>:11:7

3. JS 中的內置錯誤3.1 Error

JavaScript 有內置的錯誤對象,它通常由try塊拋出,並在catch塊中捕獲,Error 對象包含以下屬性:

name:是錯誤的名稱,例如 「Error」, 「SyntaxError」, 「ReferenceError」 等。

我們創建一個Error 對象,並查看它的名稱和消息屬性:

const err = new Error('Error while executing the code');

console.log("name:", err.name);
console.log("message:", err.message);
console.log("stack:", err.stack);

name: Error
message: Error while executing the code
stack: Error: Error while executing the code
    at <anonymous>:1:13

JavaScript 有以下內置錯誤,這些錯誤是從 Error 對象繼承而來的

3.2 EvalError

EvalError 表示關於全局eval()函數的錯誤,這個異常不再由 JS 拋出,它的存在是為了向後兼容。

3.3 RangeError

當值超出範圍時,將引發RangeError。

➤ [].length = -1
ⓧ Uncaught RangeError: Invalid array length

3.4 ReferenceError

當引用一個不存在的變量時,將引發 ReferenceError

➤ x = x + 1;
ⓧ Uncaught ReferenceError: x is not defined

3.5 SyntaxError

當你在 JS 代碼中使用任何錯誤的語法時,都會引發SyntaxError。

➤ function() { return 'Hi!' }
ⓧ Uncaught SyntaxError: Function statements require a function name

➤ 1 = 1
ⓧ Uncaught SyntaxError: Invalid left-hand side in assignment

➤ JSON.parse("{ x }");
ⓧ Uncaught SyntaxError: Unexpected token x in JSON at position 2

3.6 TypeError

如果該值不是預期的類型,則拋出TypeError。

➤ 1();
ⓧ Uncaught TypeError: 1 is not a function

➤ null.name;
ⓧ Uncaught TypeError: Cannot read property 'name' of null

3.7 URIError

如果以錯誤的方式使用全局 URI 方法,則會拋出URIError。

➤ decodeURI("%%%");
ⓧ Uncaught URIError: URI malformed

4. 定義並拋出自定義錯誤

我們也可以用這種方式定義自定義錯誤。

class CustomError extends Error {
  constructor(message) {
    super(message);
    this.name = "CustomError";
  } 
};

const err = new CustomError('Custom error while executing the code');

console.log("name:", err.name);
console.log("message:", err.message);

name: CustomError
message: Custom error while executing the code

我們還可以進一步增強CustomError對象以包含錯誤代碼

class CustomError extends Error {
  constructor(message, code) {
    super(message);
    this.name = "CustomError";
    this.code = code;
  } 
};

const err = new CustomError('Custom error while executing the code', "ERROR_CODE");

console.log("name:", err.name);
console.log("message:", err.message);
console.log("code:", err.code);

name: CustomError
message: Custom error while executing the code
code: ERROR_CODE

在try..catch塊中使用它:

try{
  try {
    null.name;
  }catch(err){
    throw new CustomError(err.message, err.name);  //message, code
  }
}catch(err){
  console.log(err.name, err.code, err.message);
}

CustomError TypeError Cannot read property 'name' of null

相關焦點

  • Try..Catch 不能捕獲的錯誤有哪些?注意事項又有哪些?
    try塊包含我們需要檢查的代碼 關鍵字throw用於拋出自定義錯誤 catch塊處理捕獲的錯誤 finally 塊是最終結果無論如何,都會執行的一個塊,可以在這個塊裡面做一些需要善後的事情1.1 try每個try塊必須與至少一個catch或finally塊,否則會拋出SyntaxError錯誤。
  • 【JavaScript】try..catch 不能捕獲的錯誤有哪些?注意事項又有哪些?
    1.使用 try..catch..finally..throw在 JS 中處理錯誤,我們主要使用try、catch、finally和throw關鍵字。 Missing catch or finally after try1.2 try..catch建議將try與catch塊一起使用,它可以優雅地處理try塊拋出的錯誤。
  • php中try catch捕獲異常實例詳解
    具體方法分析如下:php中try catch可以幫助我們捕獲程序代碼的異常了,這樣我們可以很好的處理一些不必要的錯誤了,感興趣的朋友可以一起來看看。PHP中try{}catch{}語句概述PHP5添加了類似於其它語言的異常處理模塊。在 PHP 代碼中所產生的異常可被 throw語句拋出並被 catch 語句捕獲。
  • js錯誤處理,"try..catch"
    但是有一種語法結構 try..catch,它使我們可以「捕獲(catch)」錯誤,因此腳本可以執行更合理的操作,而不是死掉。} catch (err) {  // 錯誤捕獲}它按照以下步驟執行:如果這裡沒有錯誤,則忽略 catch(err):執行到 try 的末尾並跳過 catch 繼續執行。如果這裡出現錯誤,則 try 執行停止,控制流轉向 catch(err) 的開頭。
  • 跟我學java編程—使用try和catch捕獲異常
    前面了解了Java異常和異常處理類,本節講述如何使用try和catch語句捕獲異常。Java程序在執行過程中如果出現異常,會自動生成一個異常對象,該異常對象將被自動提交給JVM,當JVM接收到異常對象時,會尋找能處理這一異常的代碼,並把當前異常對象交給其處理,這一過程稱為捕獲(catch)異常。如果JVM找不到可以捕獲異常的方法,則運行時系統將終止,相應的Java程序也將退出。
  • try...catch 對JS的性能影響有多大?
    什麼時候應該使用 try...catch,它對性能的影響又有多大?錯誤的分類在開發過程中,我們一定會遇到很多「錯誤」,可以大致分為以下這麼幾種:通常來說,處理這些錯誤有兩種方式。: any}如此,調用方在獲得返回值後,根據錯誤標識欄位,即可判斷是否出錯。兩種處理方式的區別錯誤檢測的強制性try...catch 本質上是給了開發者選擇:你可以處理這個異常,也可以選擇不處理。如果不處理,則異常會拋至全局作為「未經捕獲的異常」。
  • 面試官:用一句話描述 JS 異常是否能被 try catch 捕獲到 ?
    之前代碼報錯的時候,線程執行未進入 try catch,那麼無法捕捉異常。比如語法異常(syntaxError),因為語法異常是在語法檢查階段就報錯了,線程執行尚未進入 try catch 代碼塊,自然就無法捕獲到異常。
  • 【編碼】C++異常處理 - try,catch,throw,finally的用法
    這與拋出localWidget異常有很大不同.不論通過傳值捕獲異常還是通過引用捕獲(不能通過指針捕獲這個異常,因為類型不匹配)都將進行lcalWidget的拷貝操作,也就說傳遞到catch子句中的是localWidget的拷貝.必須這麼做,因為當localWidget離開了生存空間後,其析構函數將被調用.
  • 每日乾貨丨C++異常處理入門(try和catch)
    try和catch都是 C++ 中的關鍵字,後跟語句塊,不能省略{ }。try 中包含可能會拋出異常的語句,一旦有異常拋出就會被後面的 catch 捕獲。從 try 的意思可以看出,它只是「檢測」語句塊有沒有異常,如果沒有發生異常,它就「檢測」不到。catch 是「抓住」的意思,用來捕獲並處理 try 檢測到的異常;如果 try 語句塊沒有檢測到異常(沒有異常拋出),那麼就不會執行 catch 中的語句。
  • Java筆試面試總結—try、catch、finally語句中有return 的各類情況
    >      return "try塊的返回值";    }catch (Exception e){      System.out.println("捕獲到了異常");      return "catch的返回值";    }  }執行結果:try開始捕獲到了異常catch
  • 面試須知(8):C#的異常處理機制(try...catch...finally)
    在 C# 語言中異常與異常處理語句包括三種形式,即 try catch、try finally、try catch finally。在上述三種異常處理的形式中所用到關鍵字其含義如下:try:一個 try 塊標識了一個將被激活的特定的異常的代碼塊。後跟一個或多個 catch 塊。
  • 世上最真情的愛戀就是你在try我在catch(這我酸了……)
    本文轉載自【微信公眾號:五角錢的程式設計師,ID:xianglin965】經微信公眾號授權轉載,如需轉載與原文作者聯繫一起學習、成長、溫情的熱愛生活有一種暖男叫catch,有一種真愛叫try—catch,世上最真情的愛戀就是你在try我在catch,無論你發什麼脾氣,我都靜靜的接受,默默地處理,不管你有什麼錯,我都會原諒你,愛著你。
  • 為什麼不推薦使用try-catch-finally處理Java異常?
    而關閉的方法有很多種。比如finalizer、try-catch-finally、try-with-resources等等。1、finally不是必要條件也就是說try-catch-finally中,可以只有try-catch,也可以只有try-finally。
  • 一文詳解 Try 和異常的區別
    try、catch、finally基本介紹1. trytry 語句是用來進行錯誤處理或者清理錯誤的代碼塊。當 try 中的代碼發生錯誤時,如果存在catch代碼塊,那麼它將會被將會被執行,如果只存在 finally 代碼塊的話,他將在 try 代碼塊執行完畢後執行,如果存在 catch 代碼塊和 finally 代碼塊的話,finally 代碼塊將在 catch 代碼塊執行完畢後執行。finally 主要的作用是不管 try 中是否發生錯誤,都要執行清理代碼。
  • (原創)手把手,帶你學習Swift的do-try-catch錯誤處理模式
    6.7 Swift的do-try-catch錯誤處理模式 [Swift原創教程]1. 本節課將為你解析異常捕捉語句,該語句主要用於對異常和錯誤進行監測和處理。同時通過一個食品出售的實例,演示異常捕捉語句的具體用法。
  • 枸杞的食用注意事項有哪些?哪些人不能吃枸杞
    原標題:枸杞的食用注意事項有哪些?哪些人不能吃枸杞   枸杞在許多人的家裡都會有,在平時喝茶的時候,也會經常放一點,這樣茶就會有一點甜味,枸杞一般被認為是一種可以明目的食物,所以上班族也喜歡喝枸杞,那麼大家知道枸杞的飲食禁忌是什麼嗎?感興趣的朋友們一起來看看吧。   枸杞的飲食禁忌是什麼?
  • 轉租的注意事項有哪些,轉租有哪些注意事項?
    租房一般會籤訂一個長期的租房合同,一般在剛開始租房的時候都會趨向半年或者一年長期籤署,但是往往籤署長期的合同經常會有一些外在因素造成沒有辦法繼續租賃下去,需要轉租。那麼轉租的注意事項有哪些,轉租有哪些注意事項?
  • java的try、catch、finally
    catch的作用就是當try塊中的代碼拋出異常時,而這個異常又正好是自己負責的,那這個catch塊就開始工作了。所以,catch是流水線質檢工人,只負責自己檢驗的那部分工作。下面看看有多個質檢工人的流水線。其中每個catch後面跟的異常類型,就是這個質檢工人負責的異常類型。這個質檢工人只負責try塊拋出的這個類型的異常。一個try是可以有多個catch塊的。
  • 一分鐘打通JavaScript的try-catch
    1.2 利用try-catch來解決錯誤 從圖1-2我們能看出當try中的代碼發生錯誤時,利用catch可以進行異常處理,但是當try塊中的某一塊代碼發生錯誤時,後面的代碼將不會執行,所以我們應該儘量減少try塊的代碼
  • 為什麼推薦使用try-with-resources代替try-finally
    而關閉的方法有很多種。比如finalizer、try-catch-finally、try-with-resources等等。1、finally不是必要條件也就是說try-catch-finally中,可以只有try-catch,也可以只有try-finally。