浅析JavaScript的7种异常类型及如何快速排查

  无论是浏览器控制台还是Node.js的服务端,我们会在各种地方看到JavaScript异常,异常处理是编写程序必备的基础能力,在学习异常处理之前,了解 JavaScript 中的几种异常类型是非常有必要的。

1、Error

  Error 是最基本的错误类型,其他的错误类型都继承自该类型。

  Error 对象主要有两个重要属性 message 和 name 分别表示错误信息和错误名称。

程序运行过程中抛出的异常一般都有具体的类型,Error 类型一般都是开发人员自己抛出的异常。

try {
  throw new Error('ConardLi抛出的异常');
} catch (error) {
  console.log(error);
}

2、SyntaxError - 语法错误

  语法错误也称为解析错误。语法错误在任何编程语言中都是最常见的错误类型,表示不符合编程语言的语法规范。

  JavaScript 是一门解释性语言,执行一段代码时需要经历 词法分析 -> 语法分析 -> 语法树 就可以开始解释执行了:

  词法分析是将字符流(char stream)转换为记号流(token stream)、语法分析阶段会将记号流(token stream)生成抽象语法树(AST)。

  在这两个阶段,如果 Javascript引擎发现了预期之外/无法抓换的 token,或者 token 顺序和预期不一致时,就会抛出 SyntaxError

  因此 SyntaxError 应该和其他类型的异常区分开,此类异常发生在 JavaScript 解析/编译时,此类异常一旦发生,导致整个js文件都无法执行,而其他异常发生在代码运行时,这一类的错误会导致在错误出现的那一行之后的代码无法执行,但在那一行之前的代码不会受到影响。

  SyntaxError 类型的错误通常是语法错误,遇到这中错误时建议通过你所用的 IDE 排查,比如 VSCode 能够直接跳出这类型的错误提示。

  如下图,VSCode 用红色波浪线提示 family 对象有错误,当出现错误时会建议不要只检查当前行,错误可能会存在于上下文中(有可能跨多行的错误),这个例子中仔细检查可以发现在'小明'后面少了一个逗号。

1)Uncaught SyntaxError: Unexpected identifier

var person = {
  name: '小明'
  family: {
    name: '小明家'
  }
}

  语法解析错误,因为在对象结构中缺少一个逗号,除了通过在 VSCode 中查看外,也可以直接通过 Chrome Console 切换到 Source 页面查看错误行,并检查此行的上下文中是否存在语法错误

2)Uncaught SyntaxError: Unexpected end of input

function fn() {
  console.log('这是一个函数');
console.log(fn);

  语法解析错误:未预期的结束,这个例子中缺少结尾的大括号 },在编写代码时尽可能的维持正确的锁紧,将代码排列整齐之后更容易找到错误。

3)Uncaught SyntaxError: Unexpected token '}'

if (name)
  console.log('立即执行函数')
};

  语法解析错误:未预期的符号 },代码结尾多了一个 } 符号导致环境运行错误,这个错误的排查方法与上面相同,尽可能将代码排整齐并维持首尾符号的一致。

4)Uncaught SyntaxError: Identifier 'a' has already been declared

let a;
let a;

  语法解析错误:识别符号(在这里指的是变量)已经被声明,应该避免重复生命同一个变量,在 ES6 都禁止用 let、const 对变量进行重复声明,直接排除即可。

3、TypeError - 类型错误

  运行时最常见的异常,表示变量或参数不是预期类型,比如 new 关键字后面必须为构造函数、()前必须为函数。

  TypeError 是类型上的错误,同样 IDE 也不会预先提示有错误,必须在执行时才会看到,这类型的错误通常是以下几种:

  • 试图获取 undefined、null 的属性
  • 尝试调用非函式变量或表达式(例如: 'text'()

  排查重点:在获取变量前先确认其当前的数据类型及结构

1)Uncaught TypeError: Cannot read property 'a' of undefined

var a;
console.log(a.a);

  说明:在这个变量的值中无法找到其特定的属性,例如在 undefined、null 的值上是找不到其它属性的。

2)Uncaught TypeError: console.log(...) is not a function

console.log('a')
(function() {
  console.log('立即执行函数')
})()

  说明:这代码看起来是立即执行函数的错误,但是却出现了 console.log(...) is not a function。这个错误主要是因为缺少了分号。当遇到这类错误时只要在两者之间补上分号即可。

4、ReferenceError - 引用错误

  引用一个不存在的变量时发生的错误,每当我们创建或定义一个变量时,变量名称都会写入一个变量存储中心中。

  这个变量存储中心就像键值存储一样,每当我们引用变量时,它都去存储中找到 Key 并提取并返回 Value,如果我们要找的变量不在存储中,就会抛出 ReferenceError

  比如下面这个未定义的错误:

  请注意,如果我们调用的是一个已经存在的变量的一个不存在的属性,则不会抛出 ReferenceError,因为变量本身已经在存储中了,调用它不存在的属性只会是未赋值状态,也就是 undefined:

  ReferenceError 这类错误通常是指找不到引用,当出现这类错误时在 IDE 中不一定会提示现错误(除非安装了 Linter),所以在代码的运行阶段才会看到这类错误。

  排查重点:

  • 通过 Chrome 的提示改正
  • 在 JavaScript 开发环境中安装 ESLint

1)ReferenceError: a is not defined

  引用错误:由于变量 a 未定义,所以在使用这个变量时会出现未定义的提示,只要先定义好这个变量即可。

  还有另一种很常见的情况,当引用外部包时出现 “包名 + is not defined”,这种情况通常是外部资源没有被正确载入,应该确保该资源被正确的引入。

  下面的例子就是因为 jQuery 没有正确导入而导致的。

Uncaught ReferenceError: $ is not defined

5、RangeError - 边界错误

  表示超出有效范围时发生的异常,主要的有以下几种情况:

  • 数组长度为负数或超长
  • 数字类型的方法参数超出预定义范围
  • 函数堆栈调用超过最大值

  这是创建了超过长度上限的数组或执行了无法退出的递归函数所造成的错误,遇到这类问题需要重新检查代码的逻辑,是否消耗了过多的资源(内存或CPU资源)。

  排查重点:需要重新检查逻辑,如果有必要可先删除部分代码,先找出错误的片段后再进行除错。

1)Uncaught RangeError: Maximum call stack size exceeded

(function a() {
  a();
})();

  说明:在函数调用时会产生一个函数调用栈,如果在递归的过程中超过上限则会产生错误。

  这类错误也很常见,却不容易找到出错的原因,其主要原因是在递归时超过了环境的限制(使用框架时也很常见),如果遇到这错误建议改写当前调用函数的方式。

6、URIError - URL 错误

  在调用 URI 相关的方法中 URL 无效时抛出的异常,主要包括 encodeURI、decodeURI()、encodeURIComponent()、decodeURIComponent()、escape()和unescape()几个函数:

7、自定义异常

  另外,为了满足各种各样的业务需求,除了 JavaScript 已经给定的异常类型,我们还可以自定义一些异常类型,比如我们要根据不同的异常类型给用户不同的错误提示:

class UnAuthError extends Error { }
class ParamError extends Error { }

function controller() {
  throw new UnAuthError();
}

try {
  controller();
} catch (error) {
  if (error instanceof UnAuthError) {
    return '无权限';
  } 
  if (error instanceof ParamError) {
  return '参数错误';
  } 
}

 

原文地址:https://www.cnblogs.com/goloving/p/14023256.html