欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

最佳的JavaScript錯誤處理實踐

 更新時間:2016年07月16日 15:53:42   投稿:daisy  
在JavaScript中遇到處理錯誤很讓人頭疼,這篇文章整理了JavaScript錯誤處理實踐,有需要的小伙伴們可以參考。

  不管你的技術水平如何,錯誤或異常是應用程序開發(fā)者生活的一部分。Web開發(fā)的不連貫性留下了許多錯誤能夠發(fā)生并確實已經(jīng)發(fā)生的地方。解決的關鍵在于處理任何不可預見的(或可預見的錯誤),來控制用戶的體驗。利用JavaScript,就有多種技術和語言特色可以用來正確地解決任何問題。
  在 JavaScript 中處理錯誤很危險。如果你相信墨菲定律,會出錯的終究會出錯!在這篇文章中,我會深入研究 JavaScript 中的錯誤處理。我會涉及到一些陷阱和好的實踐。最后我們會討論異步代碼處理和 Ajax。

  我認為 JavaScript 的事件驅(qū)動模型給這門語言添加了豐富的含義。我認為這種瀏覽器的事件驅(qū)動引擎和報錯機制沒什么區(qū)別。每當發(fā)生錯誤,就相當于在某個時間點拋出一個事件。理論上說,我們在 JavaScript 中可以像處理普通事件一樣去處理拋錯事件。如果對你來說這聽起來很陌生,那請集中注意力開始學習下面的旅程。本文只針對客戶端的 JavaScript。

  示例

  本文章中用到的代碼示例在 GitHub 上可以得到,目前頁面是這個樣子的:


 

  單擊每個按鈕都會引發(fā)一個錯誤。它模擬產(chǎn)生一個 TypeError 型的 exception。下面是對這樣一個模塊的定義及單元測試。

function error() {
 var foo = {};
 return foo.bar();
}

  首先,這個函數(shù)定義了一個空的對象 foo。請注意,bar() 方法沒有在任何地方定義。我們用單元測試來驗證這確實會引發(fā)報錯。

it('throws a TypeError', function () {
 should.throws(target, TypeError);
});

  這個單元測試使用 Mocha 和 Should.js 庫中的測試斷言。Mocha 是一個運行測試框架,should.js 是一個斷言庫。如果你不太熟悉,可以在線免費瀏覽他們的文檔。一個測試用例通常以 it('description') 開始,以 should 中斷言的通過或者失敗結(jié)束。用這套框架的好處就是可以在 node 里進行單元測試,而不必非在瀏覽器里。我建議大家認真對待這些測試,因為它們驗證了 JavaScript 中很多關鍵的基本概念。

  如上所示, error() 定義了一個空對象,然后試圖去調(diào)用其中的方法。因為在這個對象中不存在 bar() 這個方法,它會拋出一個異常。相信我,在像 JavaScript 這種動態(tài)語言里,任何人都有可能犯這類錯誤。

  不好的示范

  先來看看不佳的錯誤處理方式。我處理錯誤的動作抽象出來,綁定在按鈕上。下面是處理程序的單元測試的樣子:

function badHandler(fn) {
 try {
  return fn();
 } catch (e) { }
 return null;
}

  這個處理函數(shù)接收一個回調(diào)函數(shù) fn 作為依賴。接著在處理程序的內(nèi)部調(diào)用了這個函數(shù)。這個單元測試示例了如何使用這個方法。

it('returns a value without errors', function() {
 var fn = function() {
  return 1;
 };
 var result = target(fn);
 result.should.equal(1);
});

it('returns a null with errors', function() {
 var fn = function() {
  throw Error('random error');
 };
 var result = target(fn);
 should(result).equal(null);
});

  就像你看到的那樣,如果發(fā)生了錯誤,這個詭異的處理方法會返回一個 null。這個回調(diào)函數(shù) fn() 會指向一個合法的方法或者錯誤。下面的單擊處理事件完成了剩下的部分。

(function (handler, bomb) {
 var badButton = document.getElementById('bad');

 if (badButton) {
  badButton.addEventListener('click', function () {
   handler(bomb);
   console.log('Imagine, getting promoted for hiding mistakes');
  });
 }
}(badHandler, error));

  糟糕的是我剛剛得到的是個 null。這讓我在想確定到底發(fā)生了什么錯誤的時候非常迷茫。這種發(fā)生錯誤就沉默的策略覆蓋了從用戶體驗設計到數(shù)據(jù)損壞的各個環(huán)節(jié)。隨之而來令人沮喪的一面就是,我必須花費好幾個小時調(diào)試但是卻看不到 try-catch 代碼塊里的錯誤。這種詭異的處理隱藏掉了代碼中所有的報錯,它假設一切都是正常的。這在某些不注重代碼質(zhì)量的團隊中,能夠順利的執(zhí)行。但是,這些被隱藏的錯誤最終會迫使你花幾個小時來調(diào)試代碼。在一種依賴于調(diào)用棧的多層解決方案中,有可能可以確定錯誤來自于何處??赡茉跇O少數(shù)情況下對 try-catch 做故障靜默處理是合適的。但是如果遇到錯誤就去處理,也不是一個好方案。

  這種失敗即沉默的策略會促使你在代碼中對錯誤做更好的處理。JavaScript 提供了更優(yōu)雅的方式來處理這類問題。

  不易讀的方案

  繼續(xù),接下來來看看不太好理解的處理方式。我將會跳過與 DOM 緊耦合的部分。這部分與我們剛剛看過的不好的處理方式?jīng)]什么不同。重點是下面單元測試中處理異常的部分。

function uglyHandler(fn) {
 try {
  return fn();
 } catch (e) {
  throw Error('a new error');
 }
}

it('returns a new error with errors', function () {
 var fn = function () {
  throw new TypeError('type error');
 };
 should.throws(function () {
  target(fn);
 }, Error);
});

  比起剛剛不好的處理方式,有一個很好的進步。異常在調(diào)用堆棧中被拋出。我喜歡的地方是錯誤從堆棧中解放出來,這對于調(diào)試有巨大的幫助。拋出一個異常,解釋器就會在調(diào)用堆棧中一級級查看找到下一個處理函數(shù)。這就提供了很多機會在調(diào)用堆棧的頂層去處理錯誤。不幸的是,因為他是一種不太好理解的錯誤,我看不到了原始錯誤的信息。所以我必須沿著調(diào)用棧找過去,找到最原始的異常。但是至少我知道拋出異常的地方發(fā)生了一個錯誤。

  這種不易讀的錯誤處理雖然無傷大雅但是卻使得代碼難以理解。讓我們看看瀏覽器如何處理錯誤的。

  調(diào)用棧

  那么,拋出異常的一種方式就是在調(diào)用堆棧的頂層添加 try...catch 代碼塊。比如說:

function main(bomb) {
 try {
  bomb();
 } catch (e) {
  // Handle all the error things
 }
}

  但是,記得我說過瀏覽器是事件驅(qū)動的嗎?是的,JavaScript 中的一個異常不過就是一個事件。解釋器會在發(fā)生異常當前的上下文處停止程序,并拋出異常。為了證實這一點,下面寫了一個我們能夠看到的全局的事件處理函數(shù) onerror。它看上去就是這個樣子:

window.addEventListener('error', function (e) {
 var error = e.error;
 console.log(error);
});

  這個事件處理函數(shù)在執(zhí)行環(huán)境中捕獲錯誤。錯誤事件會在各種各樣的地方產(chǎn)生各種錯誤。這種方式的重點是在代碼中集中處理錯誤。就像其他的事件一樣,你可以用一個全局的處理函數(shù)去處理各種不同的錯誤。這使得錯誤處理只有一個單一的目標,如果你遵守 SOLID (single responsibility 單一職責, open-closed 開閉, Liskov substitution 代換, interface segregation 界面分離 and dependency inversion 依賴倒置) 原則。你可以在任何時候注冊錯誤處理函數(shù)。解釋器會循環(huán)執(zhí)行這些函數(shù)。代碼從充滿 try...catch 的語句中解放出來,變得易于調(diào)試。這種做法的關鍵是像處理 JavaScript 普通事件一樣處理發(fā)生的錯誤。

  現(xiàn)在,有了一種方法,用全局處理函數(shù)來顯示出調(diào)用棧,我們可以用它來做什么?終究,我們要利用調(diào)用棧。

  記錄下調(diào)用棧

  調(diào)用棧在處理修復 bug 上非常有用。好消息是瀏覽器提供了這個信息。就算目前,error 對象的 stack 屬性并不是標準,但是在比較新的瀏覽器里都普遍支持這個屬性。

  所以,我們能夠做的很酷的事情就是把它給服務器打印出來:

window.addEventListener('error', function (e) {
 var stack = e.error.stack;
 var message = e.error.toString();
 if (stack) {
  message += '\n' + stack;
 }
 var xhr = new XMLHttpRequest();
 xhr.open('POST', '/log', true);
 xhr.send(message);
});

  在代碼示例中可能不太明顯,但這個事件處理程序會被前面的錯誤代碼觸發(fā)。如上所述,每個處理程序都有一個單一的目的,它使代碼 DRY(don't repeat yourself 不重復制造輪子)。我感興趣的是如何在服務器上捕獲這些消息。

  下面是 node 運行時的截圖:


 

  調(diào)用堆棧對調(diào)試代碼很有幫助。永遠不要低估調(diào)用棧的作用。

  異步處理

  哦,處理異步代碼相當危險!JavaScript 將異步代碼從當前的執(zhí)行環(huán)境中帶出來。這意味著下面這種 try...catch 語句有個問題。

function asyncHandler(fn) {
 try {
  setTimeout(function () {
   fn();
  }, 1);
 } catch (e) { }
}

  這個單元測試還有剩下的部分:

it('does not catch exceptions with errors', function () {
 var fn = function () {
  throw new TypeError('type error');
 };
 failedPromise(function() {
  target(fn);
 }).should.be.rejectedWith(TypeError);
});

function failedPromise(fn) {
 return new Promise(function(resolve, reject) {
  reject(fn);
 });
}

  我必須用一個 promise 來結(jié)束這個處理程序,以驗證異常。注意,盡管我的代碼都在 try...catch 中,但是還是出現(xiàn)了未處理的異常。是的,try...catch 只在一個單獨的執(zhí)行環(huán)境中有作用。當異常被拋出時,解釋器的執(zhí)行環(huán)境已經(jīng)不是當前的 try-catch 塊了。這一行為的發(fā)生與 Ajax 調(diào)用相似。所以,現(xiàn)在有了兩種選擇。一種可選方案就是在異步回調(diào)中捕捉異常:

setTimeout(function () {
 try {
  fn();
 } catch (e) {
  // Handle this async error
 }
}, 1);

  這種方法雖然有用,但是還有很大的提升空間。首先,try...catch 代碼塊在代碼中處處出現(xiàn)。事實上,上世紀 70 年代編程調(diào)用,他們希望他們的代碼能夠回退。另外,V8 引擎不鼓勵 在函數(shù)中使用 try…catch 代碼塊 (V8 是 Chrome 瀏覽器和 Node 使用的 JavaScript 引擎)。他們推薦在調(diào)用堆棧頂層寫這些捕獲異常的代碼塊。

  所以,這告訴我們什么?我上面說過的,在任何執(zhí)行上下文中的全局錯誤處理程序是有必要的。如果你將一個錯誤處理程序添加到 window 對象,那就是說,您已經(jīng)完成了!遵守 DRY 和 SOLID 的原則不是很好嗎?一個全局錯誤處理程序?qū)⒈3帜愕拇a易讀和干凈。

  下面就是服務器端異常處理打印的報告。注意,如果你使用的示例中的代碼,輸出的內(nèi)容可能會根據(jù)你使用的瀏覽器不同有少許不同。


 

  這個處理函數(shù)甚至可以告訴我哪個錯誤是出自于異步代碼。它告訴我錯誤來自于 setTimeout() 處理函數(shù)。太酷了!

  錯誤是每一個應用程序的一部分,但是適當?shù)腻e誤處理卻不是。在處理錯誤這件事上至少有兩種方法。一種是失敗即沉默的方案,即在代碼中忽略錯誤。另一種是快速發(fā)現(xiàn)和解決錯誤的方法,即在錯誤處停止并且重現(xiàn)。我想我已經(jīng)把我贊成哪一種及為什么贊成表達地很清楚。我的選擇:不要隱藏問題。沒有人會為你程序中的意外事件去指責你。這是可以接受的,去打斷點、重現(xiàn)、給用戶一個嘗試。在一個并不完美的世界中,給自己一個機會是很重要的。錯誤是不可避免的,為了解決錯誤你做的事情才是重要的。合理地運用JavaScript的錯誤處理特色和自動靈活的譯碼可以使用戶的體驗更順暢,同時也讓開發(fā)方的診斷工作變得更輕松。

相關文章

  • asp javascript 實現(xiàn)關閉窗口時保存數(shù)據(jù)的辦法

    asp javascript 實現(xiàn)關閉窗口時保存數(shù)據(jù)的辦法

    asp javascript 實現(xiàn)關閉窗口時保存數(shù)據(jù)的辦法...
    2007-11-11
  • 微信小程序保存多張圖片的實現(xiàn)方法

    微信小程序保存多張圖片的實現(xiàn)方法

    這篇文章主要介紹了微信小程序保存多張圖片的實現(xiàn)方法,使用promise 隊列,保存多張圖片到手機相冊,小編覺得具有一定的參考價值,有興趣的可以了解一下
    2019-03-03
  • JS組件Form表單驗證神器BootstrapValidator

    JS組件Form表單驗證神器BootstrapValidator

    做Web開發(fā)的我們,表單驗證是再常見不過的需求了。友好的錯誤提示能增加用戶體驗。今天就來看看bootstrapvalidator如何使用,感興趣的小伙伴們可以參考一下
    2016-01-01
  • js實現(xiàn)文章目錄索引導航(table of content)

    js實現(xiàn)文章目錄索引導航(table of content)

    這篇文章主要介紹了js實現(xiàn)文章目錄索引導航(table of content),需要的朋友可以參考下
    2020-05-05
  • JS使用正則表達式判斷輸入框失去焦點事件

    JS使用正則表達式判斷輸入框失去焦點事件

    這篇文章主要介紹了JS使用正則表達式判斷輸入框失去焦點事件問題,本文通過實例代碼給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-10-10
  • JS判斷日期格式是否合法的簡單實例

    JS判斷日期格式是否合法的簡單實例

    下面小編就為大家?guī)硪黄狫S判斷日期格式是否合法的簡單實例。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-07-07
  • js判斷兩個字符串是否相等的兩種方法

    js判斷兩個字符串是否相等的兩種方法

    昨天用Ajax作驗證,在前臺JS中判斷返回的字符串的值與給定的值是否相等,索性給大家總結(jié)下,這篇文章主要給大家介紹了關于js判斷兩個字符串是否相等的兩種方法,需要的朋友可以參考下
    2023-05-05
  • js switch case default 的用法示例介紹

    js switch case default 的用法示例介紹

    switch case default的用法應該存在一部分人不會使用吧,其實很簡單就是每個case后,一定要加:break;default,就相當于else,不會的朋友可以了解下
    2013-10-10
  • 僅IE9/10同時支持script元素的onload和onreadystatechange事件分析

    僅IE9/10同時支持script元素的onload和onreadystatechange事件分析

    測試結(jié)果可以看出,IE9后已經(jīng)開始支持script的onload事件了。一直以來我們判斷js文件是否已經(jīng)加載完成就是用以上的兩個事件。
    2011-04-04
  • Ionic快速安裝教程

    Ionic快速安裝教程

    Ionic 是目前最有潛力的一款 HTML5 手機應用開發(fā)框架。通過 SASS 構(gòu)建應用程序,它 提供了很多 UI 組件來幫助開發(fā)者開發(fā)強大的應用。接下來小編給大家介紹如何安裝 Ionic 在自己的電腦上搭建一個簡單的小應用,感興趣的朋友一起看看吧
    2016-06-06

最新評論