错误处理在开发和调试过程当中都显得尤其重要。有些没有进行错误处理的应用,直接就将浏览器的错误展现给了用户,极大的下降了用户体验。好比有些很 low 的网站,打开某些页面就直接弹出 "object" 这样的错误,用户看到以后一脸懵逼,心想这是什么鬼?让人感受极其的不专业。可见错误处理对一个应用来讲是多么的重要。javascript
这篇文章主要是给你们科普一些关于错误处理的知识,让你们在脑海中有一个概览。下一篇文章中我会结合具体的项目以及当前主流的一些框架,好比react, redux,来更深刻的介绍如何运用这些框架去封装一整套错误处理的解决方案。java
Error 构造对象能够实例化一个 error
对象 (也就是Error 实例),而 error 对象就是一个包含了错误信息的对象。当代码解析或者运行时发生错误,javascript 引擎就会自动产生并抛出一个 error 对象, 而后程序就中断在发生错误的地方。react
示例:redux
const error = new Error('Whoop!'); error.message; // Whoop! error.name; // Error error.stack; // "Error: Whoops! at <anonymous>:1:13"
咱们经常使用的 message
和 name
都是 error
的标准属性,因为各个浏览器厂商对 error 进行了不一样的扩展,因此在不一样的浏览器中,error 也有不一样的属性和方法, 非标准属性中咱们经常使用的是 stack
属性(不少浏览器都扩展了这一属性), 它用来表示栈跟踪信息。数组
属性 | 含义 |
---|---|
message | 错误信息 |
name | 错误类型 |
constructor | 指定一个函数用来建立实例的原型,也就是指定构造器(建立自定义 Error 会用到) |
stack (非标准) | 栈跟踪信息 |
除了普通的 Error
构造对象之外, javascript 还实现了其余几种主要的 error 构造对象1.promise
类型 | 解析 | 实例 |
---|---|---|
EvalError | eval错误。跟全局函数 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 方法 |
URIError | decodeURI() 或者 encodeURI() 传入非法参数时,也包括 encodeURIComponent() 和 decodeURIComponent() | decodeURI('http://www.test.com&%'); encodeURIComponent('uD800'); |
这里顺便说一下 encodeURI
和 encodeURIComponent
的区别。已经了解的同窗能够忽略这一小部分,继续往前面看。浏览器
encodeURI 是对统一资源标识符 (URI)所有编码
,而 encodeURIComponent 对统一资源标识符 (URI)部分编码
。
假设一个 URI 是一个完整的 URI, 那么咱们没必要对那些在 URI 中保留的而且带有特殊含义的字符进行编码。因为 encodeURI 会替换掉全部字符,可是却不包含一些保留字符,如 "&", "+", "=" 等(这些字符在 GET 和 POST 请求中是特殊字符,须要被编码),因此 encodeURI
自己没法产生能使用与 HTTP GET 或者 POST 请求的 URI。可是咱们可使用 encodeURIComponent
来对这些字符进行编码。服务器
encodeURIComponent
转义除了字母、数字、(、)、.、!、~、*、'、-和_以外的全部字符。
为了不服务器收到不可预知的请求,对任何用户输入的做为 URI 部分的内容都须要用 encodeURIComponent 进行转义。
一般使用 throw
语句抛出错误,并用 try...catch
进行捕获。一般会把全部可能会抛出错误的代码都放在 try
语句块中,而把那些用于错误处理的代码放在 catch
块中。微信
throw
过程是阻塞的,程序会中断在第一个抛出错误的地方,因此后面的代码不会执行。session
throw new SyntaxError('this is syntax error'); throw 123; // 不执行 throw 'hi there'; // 不执行 throw true; // 不执行
catch
代码块捕获错误以后,程序不会中断,会按照正常流程继续执行下去。
try { throw new Error('Whoops!'); } catch (e) { console.log(e.name + ':' + e.message); } console.log('hello!'); // Error:Whoops! // hello!
finally
在 try...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); } }
任何没有 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 事件。
建立一个自定义类 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 是用来在 targetObject 中添加一个 .stack
属性。对该属性进行访问时,将以字符串的形式返回 Error.captureStackTrace() 语句被调用时的代码位置信息(即:调用栈历史)。
Error.captureStackTrace(targetObject[, constructorOpt])
除了 targetObject, captureStackTrace 还接受一个类型为 function 的可选参数 constructorOpt,当传递该参数时,调用栈中全部 constructorOpt 函数之上的信息(包括 constructorOpt 函数自身),都会在访问 targetObject.stack 时被忽略。当须要对终端用户隐藏内部的技术细节时, constructorOpt 参数会颇有用。
经过基类 CustomError
,咱们能够建立出更多的自定义 Error, 好比下面的 HttpRequestError
和 LoginExpiredError
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 的介绍就先讲到这里。