JavaScript 中的错误处理机制

毛病处置惩罚在开辟和调试历程中都显得尤为重要。有些没有举行毛病处置惩罚的应用,直接就将浏览器的毛病展现给了用户,极大的降低了用户体验。比方有些很 low 的网站,翻开某些页面就直接弹出 “object” 如许的毛病,用户看到以后一脸懵逼,心想这是什么鬼?让人觉得极为的不专业。可见毛病处置惩罚对一个应用来说是何等的重要。

这篇文章主如果给人人科普一些关于毛病处置惩罚的学问,让人人在脑海中有一个概览。下一篇文章中我会连系详细的项目以及当前主流的一些框架,比方react, redux,来更深切的引见怎样应用这些框架去封装一整套毛病处置惩罚的解决方案。

error 对象

Error 组织对象能够实例化一个 error 对象 (也就是Error 实例),而 error 对象就是一个包括了毛病信息的对象。当代码剖析或许运行时发作毛病,javascript 引擎就会自动发生并抛出一个 error 对象, 然后顺序就中缀在发作毛病的处所。

示例:

const error = new Error('Whoop!');
error.message; // Whoop!
error.name; // Error
error.stack; // "Error: Whoops! at <anonymous>:1:13"

我们经常运用的 messagename 都是 error 的规范属性,由于各个浏览器厂商对 error 举行了差别的扩大,所以在差别的浏览器中,error 也有差别的属性和要领, 非规范属性中我们经常运用的是 stack 属性(许多浏览器都扩大了这一属性), 它用来示意栈跟踪信息。

属性寄义
message毛病信息
name毛病范例
constructor指定一个函数用来建立实例的原型,也就是指定组织器(建立自定义 Error 会用到)
stack (非规范)栈跟踪信息

error 范例

除了一般的 Error 组织对象以外, javascript 还完成了其他几种重要的 error 组织对象1.

范例剖析实例
EvalErroreval毛病。跟全局函数 eval() 有关的毛病,在 ES5 以后已不再涌现了
InternalError内部毛病。由 JavaScript 引擎抛出的毛病
RangeError局限毛病。发作在一个数值或参数超越其正当局限,重要包括超越数组局限或许超越数字取值局限new Array(-1); (1234).toExponential(21);
ReferenceError援用毛病。一般是由于援用了一个不存在的值。a.toString();
SyntaxError语法毛病。a ? a+1;
TypeError范例毛病。一般是由于在实行特定的范例操纵时,变量的范例不符合要求。比方变量中保存着不测范例,接见不存在的要领等。var a = new {}; var a = {a:1}; a.reverse(); // 对象并没有 reverser 要领
URIErrordecodeURI() 或许 encodeURI() 传入不法参数时,也包括 encodeURIComponent() 和 decodeURIComponent()decodeURI(‘http://www.test.com&%‘); encodeURIComponent(‘uD800’);

encodeURI 和 encodeURIComponent 的区分

这里趁便说一下 encodeURIencodeURIComponent 的区分。已相识的同砚能够疏忽这一小部份,继承往前面看。

encodeURI 是对一致资本标识符 (URI) 悉数编码,而 encodeURIComponent 对一致资本标识符 (URI) 部份编码

假定一个 URI 是一个完全的 URI, 那末我们没必要对那些在 URI 中保存的而且带有特别寄义的字符举行编码。由于 encodeURI 会替换掉一切字符,然则却不包括一些保存字符,如 “&”, “+”, “=” 等(这些字符在 GET 和 POST 要求中是特别字符,需要被编码),所以 encodeURI 本身没法发生能运用与 HTTP GET 或许 POST 要求的 URI。然则我们能够运用 encodeURIComponent 来对这些字符举行编码。

encodeURIComponent 转义除了字母、数字、(、)、.、!、~、*、’、-和_以外的一切字符。

为了防止服务器收到不可预知的要求,对任何用户输入的作为 URI 部份的内容都需要用 encodeURIComponent 举行转义。

抛出和捕捉毛病 throw and try…catch

一般运用 throw 语句抛出毛病,并用 try...catch 举行捕捉。一般会把一切能够会抛出毛病的代码都放在 try 语句块中,而把那些用于毛病处置惩罚的代码放在 catch 块中。

throw 语句

throw 历程是壅塞的,顺序会中缀在第一个抛出毛病的处所,所以背面的代码不会实行。

throw new SyntaxError('this is syntax error'); 
throw 123; // 不实行
throw 'hi there'; // 不实行
throw true;  // 不实行

catch 语句

catch 代码块捕捉毛病以后,顺序不会中缀,会根据一般流程继承实行下去。

try {
  throw new Error('Whoops!');
} catch (e) {
  console.log(e.name + ':' + e.message);
}
console.log('hello!');

// Error:Whoops!
// hello!

finally 语句

finallytry...catch 中是可选的,然则一旦运用了它,它内里的代码就肯定会被实行,也就是说不论 try 语句块中的代码是不是一般实行,finnaly 都邑被实行。正如下面的代码, 纵然在 try 中资本被壅塞,由于我们在 finnaly 中实行了封闭操纵,文件末了照样会被封闭。

openMyFile()
try {
   // 壅塞资本
   writeMyFile(theData);
} finally {
   closeMyFile(); // 一直会封闭资本
}

处置惩罚一个特定的毛病。

    try {
      foo.bar();
    } catch (e) {
      switch (e.name) {
        case 'RangeError':
          //do something
          console.log('RangeError: ' + e.message);
          break;
        case 'ReferenceError':
          //do something
          console.log('ReferenceError: ' + e.message);
          break;
        default:
          console.log(e.name + ':' + e.message);
      }
    }

error 事宜

任何没有 catch 的毛病都邑触发 window 对象的 error 事宜。

error 事宜能够吸收三个参数:毛病音讯、毛病地点的 URL 和行号。你能够经由过程以下两种体式格局给 window 绑上 error 事宜2

  // message: 毛病音讯, source: 发作毛病文件的 URL, lineno: 毛病行号

  // 要领一
  window.onerror = function(messageOrEvent, source, lineno, colno, error) {
    alert(messageOrEvent, source, lineno, colno, error);
  }
  
  or 
  
  window.onerror = console.log;
  throw new Error('whoops!');
  
  // 要领二 
  window.addEventListener('error', function(errorEvent){
      alert(errorEvent.error);
  });

在实际情况中,error 事宜并不经常运用(然则在某些情况下,如微信端的调试,error 事宜照样挺有效的),由于我们照样愿望一切的非常都能获得很好的处置惩罚,而不是把毛病交给浏览器。但有的时刻并非一切的毛病都能够被扑获,而且某些营业场景会运用到追踪浏览器报错的东西,这时刻能够就需要将浏览器的毛病抛出去,所以在这类情况下也需要去全局监听 error 事宜。

自定义毛病范例 Custom Error Types

建立一个自定义类 CustomError, 以方便去扩大更多的自定义 Error

CustomError.js

class CustomError extends Error {
  constructor(message) {
    super(message);
    this.name = this.constructor.name;
    if (typeof Error.captureStackTrace === 'function') {
      // 在浏览器范畴,除了运用V8引擎的 Chrome,
      // 别的浏览器中不存在 Error.captureStackTrace()这一接口,
      // 所以在这里做一个前提推断。
      Error.captureStackTrace(this, this.constructor); // 返回挪用客栈信息, 用于在 error 对象上增加合理的 stack 属性。
    } else {
      this.stack = new Error(message).stack;
    }
  }
}

Error.captureStackTrace

Error.captureStackTrace 是用来在 targetObject 中增加一个 .stack 属性。对该属性举行接见时,将以字符串的情势返回 Error.captureStackTrace() 语句被挪用时的代码位置信息(即:挪用栈汗青)。

Error.captureStackTrace(targetObject[, constructorOpt])

除了 targetObject, captureStackTrace 还接收一个范例为 function 的可选参数 constructorOpt,当通报该参数时,挪用栈中一切 constructorOpt 函数之上的信息(包括 constructorOpt 函数本身),都邑在接见 targetObject.stack 时被疏忽。当需要对终端用户隐蔽内部的技术细节时, constructorOpt 参数会很有效。

扩大自定义 Error 范例

经由过程基类 CustomError,我们能够建立出更多的自定义 Error, 比方下面的 HttpRequestErrorLoginExpiredError

HttpRequestError.js

class HttpRequestError extends CustomError {
  constructor(message, requestId, code, httpStatusCode) {
    const defaultAPIErrorMessage = () => {
      // 搜检是不是有网
      return window.navigator.onLine ? 'Something wrong' : 'No connection';
    };

    message = message || defaultAPIErrorMessage();
    super(message);

    this.requestId = requestId;
    this.code = code;
    this.httpStatusCode = httpStatusCode;
  }
}

throw new HttpRequestError(null, 'requestId', 'code', 'httpStatusCode');

LoginExpiredError.js

class LoginExpiredError extends CustomError {
  constructor(message) {
    message = message || 'Your session has expired!';

    super(message);
  }
}

throw new LoginExpiredError();

当我们建立了种种自定义 Error 以后,我们能够在差别的场景去运用它们了,比方在 http 要求失利的时刻抛出 HttpRequestError,并弹出对话框提醒用户。在用户登录逾期以后,抛出 LoginExpiredError,弹出对话框,并自动 logout 等等。经由过程抛出差别的 Error 范例,才让我们举行差别的扑获处置惩罚。

  const deffer = Q.deffer();

  if (expired) {
    deffer.reject(new LoginExpiredError()); // 经由过程 promise 抛出非常
  }

  if (error instanceof LoginExpiredError) { // 扑获非常
    logout();
  }

末端

关于 Error 的引见就先讲到这里。

  1. Error in MDN
  2. window.onerror
    原文作者:橘子小睿
    原文地址: https://segmentfault.com/a/1190000007384055
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞