双10期|基本对象Error及8种错误类型

2,405 阅读7分钟

双10期|基本对象Error及8种错误类型

tips:每个技术点都值得优学优写:10期

好文推荐:

约2万字-Vue源码解读汇总篇(续更)

复盘基于进阶图的30+前端知识点

写在前面

错误对象是一种特殊的基本对象。它们拥有基本的 Error 类型,同时也有多种具体的错误类型。 认识他们,对于我们合理的处理异常,抛出异常,可能是有帮助的,对于深入认识 JavaScript 也是有帮助的,错误对象(错误类型)通常也是编程语言的重要组成部分。

JavaScript 种标准的具体的错误类型,主要有 8种:

  • AggregateError(实验中)
  • EvalError
  • InternalError
  • RangeError
  • ReferenceError
  • SyntaxError
  • TypeError
  • URIError

基本对象 Error

Error 简述

通过Error的构造器可以创建一个错误对象。当运行时错误产生时, Error的实例对象会被抛出。Error对象也可用于用户自定义的异常的基础对象。

下面是一个使用 Error 的示例:

try {
  throw new Error('遇到了基本错误!')
} catch (e) {
  console.error(e.name + ': ' + e.message)
  // 打印结果:Error: 遇到了基本错误!
}

Error 的语法是:

// 中括号括起来的都是可选参数,
// message:人类可阅读的关于错误的描述
// fileName(非标准):代码中导致异常的文件的文件名
// lineNumber(非标准):代码中导致异常的代码的行号
new EvalError([message[, fileName[, lineNumber]]])

正是因为一些 js 框架,合理使用了错误类型,我们在辨析错误的时候才方便了许多, 例如 message:错误描述,fileName:发生错误的文件名,lineNumber:代码中导致异常的代码的行号。 这些错误信息的提供,大大方便了定位排查。

下面是一个包含了基本的错误信息的控制台报错截图: image.png

自定义异常类型

如果你想要自定义基于 Error 的异常类型也是被支持的,但需要注意, 在FireFox中抛出自定义类型的异常可能会显示不正确的行号和文件名。

下面是一个示例:

function MyError (message) {
  this.name = 'MyError';
  this.message = message || 'Default Message';
  this.stack = (new Error()).stack;
}

MyError.prototype = Object.create(Error.prototype);
MyError.prototype.constructor = MyError;

try {
  throw new MyError();
} catch (e) {
  console.log(e.name);     // 'MyError'
  console.log(e.message);  // 'Default Message'
}

try {
  throw new MyError('custom message');
} catch (e) {
  console.log(e.name);     // 'MyError'
  console.log(e.message);  // 'custom message'
}

AggregateError

当多个错误需要包装在一个错误中时,可以考虑使用 AggregateError。

tips:这是一个实验中的功能.此功能某些浏览器尚在开发中,请参考浏览器兼容性, 在不同浏览器中使用适合的前缀。由于该功能对应的标准文档可能被重新修订, 所以在未来版本的浏览器中该功能的语法和行为可能随之改变。

语法:

// errors 错误的描述,默认为空。
// message 错误的提示信息。
new AggregateError(errors[, message])

tips:语法习惯上使用[]把可选内容包裹起来,例如上面,包括那个逗号都是可选的,都被[]包裹了。 这不仅仅是 javascript 这样做,所以什么是可选的,通常只要看是否被[]即可, 当然这是在遵守规范或约定的前提下。

下面是一个创建 AggregateError 的示例

try {
  throw new AggregateError([
    new Error("some error"),
  ], 'Hello');
} catch (e) {
  console.log(e instanceof AggregateError); // true
  console.log(e.message);                   // "Hello"
  console.log(e.name);                      // "AggregateError"
  console.log(e.errors);                    // [ Error: "some error" ]
}

下面是一个捕获 AggregateError 的示例

Promise.any([
  Promise.reject(new Error("some error")),
]).catch(e => {
  console.log(e instanceof AggregateError); // true
  console.log(e.message);                   // "All Promises rejected"
  console.log(e.name);                      // "AggregateError"
  console.log(e.errors);                    // [ Error: "some error" ]
});

EvalError

EvalError 表示一个关于 eval 函数的错误。 EvalError 不在当前ECMAScript规范中使用, 因此不会被运行时抛出. 但是对象本身仍然与规范的早期版本向后兼容。

语法:

// message:错误描述
// fileName:代码中导致异常的文件的文件名
// lineNumber:代码中导致异常的代码的行号
new EvalError([message[, fileName[, lineNumber]]])

下面是一个创建 EvalError 的示例

try {
  throw new EvalError('Hello', 'someFile.js', 10);
} catch (e) {
  console.log(e instanceof EvalError); // true
  console.log(e.message);              // "Hello"
  console.log(e.name);                 // "EvalError"
  console.log(e.fileName);             // "someFile.js"
  console.log(e.lineNumber);           // 10
  console.log(e.columnNumber);         // 0
  console.log(e.stack);                // "@Scratchpad/2:2:9\n"
}

tips:全局的 EvalError 对象本身不包含任何方法, 然而它通过原型链继承了一些方法。 也支持通过 prototype 向 EvalError 对象中添加自定义属性。

InternalError

InternalError 对象表示出现在 JavaScript引 擎内部的错误。 当 JavaScript 引擎出现内部错误时将会抛出InternalError。 例如: "InternalError: too much recursion"(内部错误:递归过深)。

tips:需要特别注意的是该特性是非标准的,所以应该谨慎在生产环境中使用它。

语法:

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

一些适用场景:

  • "too many switch cases"(过多case子句);
  • "too many parentheses in regular expression"(正则表达式中括号过多);
  • "array initializer too large"(数组初始化器过大);
  • "too much recursion"(递归过深)。

RangeError

RangeError 对象表示范围越界错误,当一个值不在其所允许的范围或者集合中,可考虑使用它。

例如,试图传递一个 number 参数给一个范围内不包含该 number 的函数时则会引发 RangeError。 当传递一个不合法的 length 值作为 Array 构造器的参数创建数组, 或者传递错误值到数值计算方法(Number.toExponential(), Number.toFixed() ,Number.toPrecision()),会出现 RangeError。

语法:

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

下面是一个使用 RangeError 的示例:

let MIN = 3, MAX = 10
let check = function (num) {
  if (num < MIN || num > MAX) {
    throw new RangeError('参数必须在这个区间:(' + MIN + ',' + MAX + ')');
  }
};

try {
  check(500);
} catch (e) {
  if (e instanceof RangeError) {
    // 处理越界错误
    console.error(e.name + ': ' + e.message)
  }
}

ReferenceError

ReferenceError 用来表示引用类错误,ReferenceError 对象代表当一个不存在的变量被引用时发生的错误。 例如,当你尝试引用一个未被定义的变量时,将会抛出一个 ReferenceError 。

语法:

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

下面是一个创建 ReferenceError 的示例

try {
  throw new ReferenceError('引用错误', 'that-file.js', 250)
} catch (e) {
  console.log(e instanceof ReferenceError) // true
  console.log(e.message)                   // 引用错误
  console.log(e.name)                      // "ReferenceError"
}

一个捕获 ReferenceError 的示例

try {
  let a = username
} catch (e) {
  console.log(e instanceof ReferenceError) // true
  console.log(e.message)                   // username is not defined
  console.log(e.name)                      // ReferenceError
  console.log(e.stack)                     // ReferenceError: username is not defined at ...
}

JavaScript 种有一个“奇怪”的现象,就是“变量提升”,但这种提升又是有区别的, 例如在使用 let 声明一个变量前就使用它会被抛出一个 ReferenceError 异常,而使用 var 则不会抛出 ReferenceError。

下面是一个会抛出 ReferenceError 的示例。

console.log(x) // ReferenceError
let x = 2

image.png

SyntaxError

SyntaxError 语法错误,代表尝试解析语法上不合法的代码的错误。 当 Javascript 语言解析代码时,Javascript 引擎发现了不符合语法规范的的代码时时抛出 SyntaxError。

语法:

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

下面是一个捕获 SyntaxError 的示例

try {
  eval('hoo bar')
} catch (e) {
  console.log(e instanceof SyntaxError) // true
  console.log(e.message)                // Unexpected identifier
  console.log(e.name)                   // SyntaxError
  console.log(e.stack)                  // SyntaxError: Unexpected identifier at ...
}

TypeError

TypeError 表示类型错误,表示值的类型不是预期类型时发生的错误。 例如,当传入函数的操作数或参数的类型并非操作符或函数所预期的类型时,将抛出一个 TypeError 类型错误。

语法:

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

下面是一个捕获 TypeError 的示例

try {
  null.f()
} catch (e) {
  console.log(e instanceof TypeError) // true
  console.log(e.message)              // Cannot read properties of null (reading 'f')
  console.log(e.name)                 // TypeError
  console.log(e.stack)                // TypeError: Cannot read properties of null (reading 'f') at ...
}

下面是一张关于先使用函数表达式,再去声明会被抛出 TypeError 的截图。

image.png

URIError

URIError 对象用来表示,以一种错误的方式使用全局 URI 处理函数,而产生的错误。 当向全局 URI 处理函数传递一个不合法的 URI 时,URIError 错误会被抛出。

语法:

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

下面是一个使用 URIError 的示例

try {
  decodeURIComponent('%@资源地址')
} catch (e) {
  console.log(e instanceof URIError) // true
  console.log(e.message)             // URI malformed(畸形的,难看的)
  console.log(e.name)                // URIError
  console.log(e.stack)               // URIError: URI malformed at decodeURIComponent (<anonymous>) ...
}

写在后面

关于 error 的使用,可能在一些应用的常规页面开发未必用得上, 但 JavaScript 是高级编程语言,它能做更多的事。就像是在写 java 接口时处理业务逻辑, 抛出适当的异常和捕获异常,可能是比较常见的事。如果 JavaScript 也做那样的事情, 那么 Error 相关的知识,结合 throw 语句抛出一个异常并且用 try...catch 语句捕获处理它, 就是常见的事了。

即使用得比较少,学习它,对于我们理解 JavaScript 和查看一些错误信息,排查定位 bug 也是有帮助的。

下面是一些在 vue3源码库 vue-next 种检索到的错误类型的应用的截图。 我曾写过 约2万字-Vue源码解读汇总篇(续更) , 而做这个的目的除了了解 vue,就是学习如何设计代码。例如本文的主角 Error 应该如何使用,我也会去该库检索学习。

image.png

image.png

image.png