成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

JavaScript之錯(cuò)誤異常探討

LdhAndroid / 3476人閱讀

摘要:提供一套錯(cuò)誤處理機(jī)制,錯(cuò)誤是干擾程序正常流程的非正常的事故。構(gòu)造函數(shù)是通用錯(cuò)誤類型,除了類型,還有等類型。瀏覽器輸出其他錯(cuò)誤類型構(gòu)造函數(shù)是繼承,實(shí)例是一致的。數(shù)值超出有效范圍數(shù)值超出有效范圍創(chuàng)建一個(gè)實(shí)例,表示錯(cuò)誤的原因無效引用。

同步發(fā)布于 https://github.com/xianshanna...

我的建議是不要隱藏錯(cuò)誤,勇敢地拋出來。沒有人會(huì)因?yàn)榇a出現(xiàn) bug 導(dǎo)致程序崩潰而羞恥,我們可以讓程序中斷,讓用戶重來。錯(cuò)誤是無法避免的,如何去處理它才是最重要的。

JavaScript 提供一套錯(cuò)誤處理機(jī)制,錯(cuò)誤是干擾程序正常流程的非正常的事故。而沒人可以保持程序沒有 bug,那么上線后遇到特殊的 bug,如何更快的定位問題所在呢?這就是我們這個(gè)專題需要討論的問題。

下面會(huì)從 JavaScript Error 基礎(chǔ)知識(shí)、如何攔截和捕獲異常、如何方便的在線上報(bào)錯(cuò)誤等方面來敘述,本人也是根據(jù)網(wǎng)上的知識(shí)點(diǎn)進(jìn)行了一些總結(jié)和分析(我只是互聯(lián)網(wǎng)的搬運(yùn)工,不是創(chuàng)造者),如果有什么錯(cuò)漏的情況,請(qǐng)?jiān)?issue 上狠狠的批評(píng)我。

這個(gè)專題目前是針對(duì)瀏覽器的,還沒考慮到 node.js,不過都是 JavaScript Es6 語法,大同小異。

什么時(shí)候 JavaScript 會(huì)拋出錯(cuò)誤呢?

一般分為兩種情況:

JavaScript 自帶錯(cuò)誤

開發(fā)者主動(dòng)拋出的錯(cuò)誤

JavaScript 引擎自動(dòng)拋出的錯(cuò)誤

大多數(shù)場(chǎng)景下我們遇到的錯(cuò)誤都是這類錯(cuò)誤。如果發(fā)生Javscript 語法錯(cuò)誤、代碼引用錯(cuò)誤、類型錯(cuò)誤等,JavaScript 引擎就會(huì)自動(dòng)觸發(fā)此類錯(cuò)誤。如下一些場(chǎng)景:

場(chǎng)景一

console.log(a.notExited)
// 瀏覽器會(huì)拋出 Uncaught ReferenceError: a is not defined

場(chǎng)景二

const a;
// 瀏覽器拋出 Uncaught SyntaxError: Missing initializer in const declaration

語法錯(cuò)誤,瀏覽器一般第一時(shí)間就拋出錯(cuò)誤,不會(huì)等到執(zhí)行的時(shí)候才會(huì)報(bào)錯(cuò)。

場(chǎng)景三

let data;
data.forEach(v=>{})
// Uncaught TypeError: Cannot read property "forEach" of undefined

手動(dòng)拋出的錯(cuò)誤

一般都是類庫開發(fā)的自定義錯(cuò)誤異常(如參數(shù)等不合法的錯(cuò)誤異常拋出)。或者重新修改錯(cuò)誤 message 進(jìn)行上報(bào),以方便理解。

場(chǎng)景一

function sum(a,b){
  if(typeof a !== "number") {
    throw TypeError("Expected a to be a number.");
  }
  if(typeof b !== "number") {
    throw TypeError("Expected b to be a number.");
  }
  return a + b;
}
sum(3,"d");
// 瀏覽器拋出 uncaught TypeError: Expected b to be a number.

場(chǎng)景二

當(dāng)然我們不一定需要這樣做。

let data;

try {
  data.forEach(v => {});
} catch (error) {
  error.message = "data 沒有定義,data 必須是數(shù)組。";
  error.name = "DataTypeError";
  throw error;
}

如何創(chuàng)建 Error 對(duì)象?

創(chuàng)建語法如下:

new Error([message[,fileName,lineNumber]])

省略 new 語法也一樣。

其中fileNamelineNumber 不是所有瀏覽器都兼容的,谷歌也不支持,所以可以忽略。

Error 構(gòu)造函數(shù)是通用錯(cuò)誤類型,除了 Error 類型,還有 TypeError、RangeError 等類型。

Error 實(shí)例

這里列舉的都是 Error 層的原型鏈屬性和方法,更深層的原型鏈的繼承屬性和方便不做說明。一些有兼容性的而且不常用的屬性和方法不做說明。

console.log(Error.prototype)
// 瀏覽器輸出 {constructor: ?, name: "Error", message: "", toString: ?}

其他錯(cuò)誤類型構(gòu)造函數(shù)是繼承 Error,實(shí)例是一致的。

屬性

Error.prototype.message

錯(cuò)誤信息, Error("msg").message === "msg"。

Error.prototype.name

錯(cuò)誤類型(名字), Error("msg").name === "Error”。如果是 TypeError,那么 name 為 TypeError。

Error.prototype.stack

Error 對(duì)象作為一個(gè)非標(biāo)準(zhǔn)的棧屬性提供了一種函數(shù)追蹤方式。無論這個(gè)函數(shù)被被調(diào)用,處于什么模式,來自于哪一行或者哪個(gè)文件,有著什么樣的參數(shù)。這個(gè)棧產(chǎn)生于最近一次調(diào)用最早的那次調(diào)用,返回原始的全局作用域調(diào)用。

這個(gè)不是規(guī)范,存在兼容性。經(jīng)測(cè)試,谷歌、火狐、Edge、safar 都支持此特性(都是在最新的版本下測(cè)試 2019-04-02),IE 不支持。

方法

Error.prototype.constructor

Error.prototype.toString

返回值格式為 ${name}: ${message}。

常用 Error 類型

除了通用的 Error 構(gòu)造函數(shù)外,JavaScript還有常見的 5 個(gè)其他類型的錯(cuò)誤構(gòu)造函數(shù)。

TypeError

創(chuàng)建一個(gè) Error 實(shí)例,表示錯(cuò)誤的原因:變量或參數(shù)不屬于有效類型。

throw TypeError("類型錯(cuò)誤");
// Uncaught TypeError: 類型錯(cuò)誤
RangeError

創(chuàng)建一個(gè) Error 實(shí)例,表示錯(cuò)誤的原因:數(shù)值變量或參數(shù)超出其有效范圍

throw RangeError("數(shù)值超出有效范圍");
// Uncaught RangeError: 數(shù)值超出有效范圍
ReferenceError

創(chuàng)建一個(gè) Error 實(shí)例,表示錯(cuò)誤的原因:無效引用

throw ReferenceError("無效引用");
// Uncaught ReferenceError: 無效引用
SyntaxError

創(chuàng)建一個(gè) Error 實(shí)例,表示錯(cuò)誤的原因:語法錯(cuò)誤。這種場(chǎng)景很少用,除非類庫定義了新語法(如模板語法)。

throw SyntaxError("語法錯(cuò)誤");
// Uncaught SyntaxError: 語法錯(cuò)誤
URIError

創(chuàng)建一個(gè) Error 實(shí)例,表示錯(cuò)誤的原因:涉及到 uri 相關(guān)的錯(cuò)誤。

throw URIError("url 不合法");
// Uncaught RangeError: url 不合法
自定義 Error 類型

自定義新的 Error 類型需要繼承 Error ,如下自定義 CustomError

function CustomError(...args){
  class InnerCustomError extends Error {
    name = "CustomError";
  }
  return new InnerCustomError(...args);
}

繼承 Error 后,我們只需要對(duì) name 做重寫,然后封裝成可直接調(diào)用的函數(shù)即可。

如何攔截 JavaScript 錯(cuò)誤?

既然沒人能保證 web 應(yīng)用不會(huì)出現(xiàn) bug,那么出現(xiàn)異常報(bào)錯(cuò)時(shí),如何攔截并進(jìn)行一些操作呢?

try…catch… 攔截

這是攔截 JavaScript 錯(cuò)誤,攔截后,如果不手動(dòng)拋出錯(cuò)誤,這個(gè)錯(cuò)誤將靜默處理。平常寫代碼如果我們知道某段代碼可能會(huì)出現(xiàn)報(bào)錯(cuò)問題,就可以使用這種方式。如下:

const { data } = this.props;
try {
  data.forEach(d=>{});
  // 如果 data 不是數(shù)組就會(huì)報(bào)錯(cuò)
} catch(err){
  console.error(err);
  // 這里可以做上報(bào)處理等操作
}
一些使用方式
十分不友好的處理方式

try...catch... 使用需要注意,try…catch… 后,錯(cuò)誤會(huì)被攔截,如果不主動(dòng)拋出錯(cuò)誤,那么無法知道報(bào)錯(cuò)位置。如下面的處理方式就是不好的。

function badHandler(fn) {
  try {
    return fn();
  } catch (err) { /**noop,不做任何處理**/ }
  return null;
}
badHandler();

這樣 fn 回調(diào)發(fā)送錯(cuò)誤后,我們無法知道錯(cuò)誤是在哪里發(fā)生的,因?yàn)橐呀?jīng)被 try…catch 了,那么如何解決這個(gè)問題呢?

相對(duì)友好但糟糕的處理方式
function CustomError(...args){
  class InnerCustomError extends Error {
    name = "CustomError";
  }
  return new InnerCustomError(...args);
}
function uglyHandlerImproved(fn) {
  try {
    return fn();
  } catch (err) { 
    throw new CustomError(err.message);
  }
  return null;
}
badHandler();

現(xiàn)在,這個(gè)自定義的錯(cuò)誤對(duì)象包含了原本錯(cuò)誤的信息,因此變得更加有用。但是因?yàn)樵俣葤伋鰜?,依然是未處理的錯(cuò)誤。

try…catch… 可以攔截異步錯(cuò)誤嗎?

這個(gè)也要分場(chǎng)景,也看個(gè)人的理解方向,首先理解下面這句話:

try…catch 只會(huì)攔截當(dāng)前執(zhí)行環(huán)境的錯(cuò)誤,try 塊中的異步已經(jīng)脫離了當(dāng)前的執(zhí)行環(huán)境,所以 try…catch… 無效。

setTimeoutPromise 都無法通過 try…catch 捕獲到錯(cuò)誤,指的是 try 包含異步(非當(dāng)前執(zhí)行環(huán)境),不是異步包含 try(當(dāng)前執(zhí)行環(huán)境)。異步無效和有效 try…catch 如下:

setTimeout

這個(gè)無效:

try {
  setTimeout(() => {
    data.forEach(d => {});
  });
} catch (err) {
  console.log("這里不會(huì)運(yùn)行");
}

下面的 try…catch 才會(huì)有效:

setTimeout(() => {
  try {
    data.forEach(d => {});
  } catch (err) {
    console.log("這里會(huì)運(yùn)行");
  }
});

Promise

這個(gè)無效:

try {
  new Promise(resolve => {
    data.forEach(d => {});
    resolve();
  });
} catch (err) {
  console.log("這里不會(huì)運(yùn)行");
}

下面的 try…catch 才會(huì)有效:

new Promise(resolve => {
  try {
    data.forEach(d => {});
  } catch (err) {
    console.log("這里會(huì)運(yùn)行");
  }
});

小結(jié)

不是所有場(chǎng)景都需要 try…catch… 的,如果所有需要的地方都 try…catch,那么代碼將變得臃腫,可讀性變差,開發(fā)效率變低。那么我需要統(tǒng)一獲取錯(cuò)誤信息呢?有沒有更好的處理方式?當(dāng)然有,后續(xù)會(huì)提到。

Promise 錯(cuò)誤攔截

Promise.prototype.catch 可以達(dá)到 try…catch 一樣的效果,只要是在 Promise 相關(guān)的處理中報(bào)錯(cuò),都會(huì)被 catch 到。當(dāng)然如果你在相關(guān)回調(diào)函數(shù)中 try…catch,然后做了靜默提示,那么也是 catch 不到的。

如下會(huì)被 catch 到:

new Promise(resolve => {
  data.forEach(v => {});
}).catch(err=>{/*這里會(huì)運(yùn)行*/})

下面的不會(huì)被 catch 到:

new Promise(resolve => {
  try {
      data.forEach(v => {});
  }catch(err){}
}).catch(err=>{/*這里不會(huì)運(yùn)行*/})

Promise 錯(cuò)誤攔截,這里就不詳細(xì)說了,如果你看懂了 try…catch,這個(gè)也很好理解。

setTimeout 等其他異步錯(cuò)誤攔截呢?

目前沒有相關(guān)的方式直接攔截 setTimeout 等其他異步操作。

如果要攔截 setTimeout 等異步錯(cuò)誤,我們需要在異步回調(diào)代碼中處理,如:

setTimeout(() => {
  try {
    data.forEach(d => {});
  } catch (err) {
    console.log("這里會(huì)運(yùn)行");
  }
});

這樣可以攔截到 setTimeout 回調(diào)發(fā)生的錯(cuò)誤,但是如果是下面這樣 try…catch 是無效的:

try {
  setTimeout(() => {
    data.forEach(d => {});
  });
} catch (err) {
  console.log("這里不會(huì)運(yùn)行");
}
如何獲取 JavaScript 錯(cuò)誤信息?

你可以使用上面攔截錯(cuò)誤信息的方式獲取到錯(cuò)誤信息。但是呢,你要每個(gè)場(chǎng)景都要去攔截一遍嗎?首先我們不確定什么地方會(huì)發(fā)生錯(cuò)誤,然后我們也不可能每個(gè)地方都去攔截錯(cuò)誤。

不用擔(dān)心,JavaScript 也考慮到了這一點(diǎn),提供了一些便捷的獲取方式(不是攔截,錯(cuò)誤還是會(huì)終止程序的運(yùn)行,除非主動(dòng)攔截了)。

window.onerror 事件獲取錯(cuò)誤信息

onerror 事件無論是異步還是非異步錯(cuò)誤(除了 Promise 錯(cuò)誤),onerror 都能捕獲到運(yùn)行時(shí)錯(cuò)誤。

需要注意一下幾點(diǎn):

window.onerror 函數(shù)只有在返回 true 的時(shí)候,異常才不會(huì)向上拋出,否則即使是知道異常的發(fā)生控制臺(tái)還是會(huì)顯示 Uncaught Error: xxxxx。如果使用 addEventListener,event.preventDefault() 可以達(dá)到同樣的效果。

window.onerror 是無法捕獲到網(wǎng)絡(luò)異常的錯(cuò)誤、

閱讀需要支付1元查看
<