Error 对象
在 JavaScript 中,我们可以使用 Error 对象来描述程序出现的错误。它的原型对象是这样的:
Error.prototype = {
name, // 错误名,默认情况为 Error
message, // 错误信息
fileName, // 调用 Error 构造器所在文件
lineNumber, // 调用 Error 构造器所在行数
columnNumber, // 调用 Error 构造器所在的列数
stack, // 用于描述调用 Error 构造器及向上的堆栈信息,每一条都会包含文件、行数及列数信息
};
Error 对象还是一个构造器,我们可以用它来实例化一个错误实例。需要注意的是,我们实例化错误对象时是和创建普通的对象的操作是一样的,它并不会自动被错误处理函数捕获,我们需要通过 throw 操作或将它传递给错误处理函数。构造器的使用语法是: new Error(message[, fileName[, lineNumber]])
。
在 JavaScript 中会遇到很多种错误,为了在处理错误时能够更加方便的分辨错误的类型,我们可以直接使用预设的 Error 的子类。这些子类主要是在 name
属性有区别,如 RangeError
生成的实例的 name
属性都是 RangeError
。其它的错误子类有:
EvalError
:表明错误与eval
有关InternalError
:表明错误与 JavaScript 引擎有,如“递归太多”RangeError
:表明错误信与数值信息或参数超出范围有关ReferenceError
:表明错误与错误引用有关SyntaxError
:表明错误是由eval()
过程中遇到的语法错误导致TypeError
:表明错误是由变量或参数类型无效导致URIError
:表明错误是由encodeURI()
或decodeURI()
传递的 URI 无效导致- …
除了使用这些内置的 Error 子类,我们还可以自定义 Error 子类:
class CustomError extends Error {
name = 'customError';
customProps = {};
}
捕获错误的方式
我们最常见的捕获错误的方式是使用 try-catch
语法来包裹我们认为可能会出现错误的代码语句,需要注意的是,这种方式只能捕获到同步代码中抛出的错误,如果是异步操作中抛出的异常是无法捕获的,如:setTimeout 的回调函数中抛出异常,我们是无法在 setTimeout 这个语句外部的 try-catch
语句中捕获到错误的,只能在 setTimeout 的回调函数里去使用 try-catch
来捕获错误。
try {
const err = new Error('something wrong');
throw err;
} catch (err) {
// do something with err
}
被抛出的异常如果没有在当前的 context 下被捕获的话会被传递到调用栈外层的 context,直至遇到 try-catch
语句或者栈顶为止。如果一直到栈顶都没有被 try-catch
捕获的话,我们还可以使用全局的错误捕获函数,如在 node 中是 process.on('uncaughtException', errorHandler)
,在 browser 中是 window.onerror = errorHandler/window.addEventListener('error', errorHandler)
。
由于无法通过 try-catch
来捕获异步函数执行中抛出的异常,所以对于异步函数而言,通常会有另外特定的错误捕获方式。根据不同的异步函数与当前函数交互方式的区别,会有不同的捕获方式:
异步 API
:在 node 中有提供很多异步 API,如fs.exists
等等。 这些异步的 API 将错误传递给我们的回调函数的方式来让调用者处理错误。fs.exists('./test.js', (err, result) => { if (err) { // handle error in here } });
promise
:Promise 构造器传入的函数是会立即执行的,但是里面同步部分的代码抛出的异常是无法被构造器外部的try-catch
捕获的,至于里面的异步代码的话既不会被 promise 捕获也不会被外层的try-catch
。因为这部分的错误会被 Promise 自己 catch 调。我们可以通过.then()
中的rejectedHandler
或者是.catch()
来捕获它。如果错误没有被正确的捕获的话,则向外冒泡到全局,在 node 中是通过process.on(unhandledrejection, handler)
、在 browser 中是通过window.onunhandledrejection = handler/window.addEventListener('unhandledrejection', handler)
来捕获。如果全局都没有被捕获的话,则会抛出相应的异常。async-await
:async-await
是 ES2017 引入的语法,async 函数的执行结果是一个 promise 对象,所以我们可以像处理普通的 promise 对象一样来捕获它的错误。而在它的内部,异步操作主要是在 await 操作符之后,而 await 操作符后的变量/函数都会被转换成一个 promise 对象,当中发生的错误会被转换成 rejected 的 promise,这些 rejected 的 promise 会阻塞 async 函数的继续执行,我们可以借助try-catch
来捕获这些 await 操作抛出的 rejected 的 promise。