JavaScript中的异常处理

任何可能出错的都会出错。 墨菲定律甚至适用于软件开发。 对于不平凡的计划,它不是一个是否的问题,而是什么时候会出问题。 标准不合规,不受支持的功能以及浏览器怪癖只是Web开发人员面临的潜在问题的一些来源。 考虑到所有可能出错的地方,JavaScript具有一种出乎意料的简单方式来处理错误-它只是放弃而无声地失败。 至少,这就是用户看到的行为。 实际上,引擎盖下发生了很多事情。

当JavaScript语句生成错误时,据说会引发 异常 。 JavaScript解释器将检查异常处理代码,而不是继续执行下一条语句。 如果没有异常处理程序,则程序从引发异常的任何函数返回。 对调用堆栈上的每个函数重复此操作,直到找到异常处理程序或到达顶级函数,从而导致程序终止。

错误对象

发生异常时,将创建并抛出代表错误的对象。 JavaScript语言定义了七种类型的内置错误对象。 这些错误类型是异常处理的基础。 下面将详细介绍每种错误类型。

错误

“错误”类型用于表示通用异常。 这种类型的异常最常用于实现用户定义的异常。 创建用户定义的异常的主题将在本文后面重新讨论。 “错误”对象通过调用其构造函数实例化,如以下示例所示。

var error = new Error("error message");

“错误”对象包含两个属性,“名称”和“消息”。 “名称”属性指定异常的类型(在本例中为“错误”)。 “ message”属性提供了对该异常的更详细描述。 “消息”是从传递给异常的构造函数的字符串中获取其值的。 其余的异常类型表示更具体的错误类型,但是它们的使用方式与常规“错误”类型相同。

RangeError

“ RangeError”异常是由超出指定范围的数字生成的。 例如,JavaScript数字具有toFixed()方法,该方法带有一个“ digits”参数,该参数表示出现在小数点后的位数。 该参数应介于0到20之间(尽管某些浏览器支持更大的范围)。 如果“ digits”的值超出此范围,则抛出“ RangeError”。 下例显示了这种情况。

var pi = 3.14159;

pi.toFixed(100000);  // RangeError

ReferenceError

访问不存在的变量时,将引发“ ReferenceError”异常。 当现有变量名称拼写错误时,通常会发生这些异常。 在下面的示例中,当访问“ bar”时发生“ ReferenceError”。 请注意,此示例假定在尝试递增操作时,“ bar”在任何活动范围中都不存在。

function foo() {
  bar++;  // ReferenceError
}

语法错误

违反JavaScript语言规则时,将引发“ SyntaxError”。 熟悉C和Java等语言的开发人员习惯于在编译过程中遇到语法错误。 但是,由于JavaScript是一种解释性语言,因此在执行代码之前不会识别语法错误。 语法错误是唯一的,因为它们是唯一无法恢复的异常类型。 下面的示例生成语法错误,因为“ if”语句缺少右花括号。

if (foo) {  // SyntaxError
  // the closing curly brace is missing

TypeError

当值不是预期的类型时,将发生“ TypeError”异常。 尝试调用不存在的对象方法是此类异常的常见原因。 下面的示例创建一个名为“ foo”的空对象,然后尝试调用其bar()方法。 由于未定义bar(),因此尝试调用时将引发“ TypeError”。

var foo = {};

foo.bar(); // TypeError

URIError

当编码URI()和decodeURI()之类的方法遇到格式错误的URI时,将引发“ URIError”异常。 以下示例在尝试对字符串“%”进行解码时生成“ URIError”。 “%”字符表示URI转义序列的开始。 由于在此示例中,“%”后面没有任何内容,因此该字符串是无效的转义序列,因此是格式错误的URI组件。

decodeURIComponent("%"); // URIError

EvalError

如果不正确使用eval()函数,则会引发“ EvalError”异常。 在最新版本的EcmaScript标准中未使用这些例外。 但是,仍然支持它们,以保持与标准旧版本的向后兼容性。

处理异常

既然我们知道异常是什么,该是时候学习如何阻止异常崩溃我们的程序了。 JavaScript通过“ try…catch…finally”语句处理异常。 通用示例语句如下所示。

try {
  // attempt to execute this code
} catch (exception) {
  // this code handles exceptions
} finally {
  // this code always gets executed
}

“ try…catch…finally”语句的第一部分是“ try”子句。 “ try”子句是强制性的,用于分隔程序员怀疑会生成异常的代码块。 “ try”子句之后必须是“ catch”和“ finally”子句中的一个或两个。

“捕获”条款

“ try…catch…finally”的第二部分是“ catch”子句。 “ catch”子句是仅在“ try”子句中发生异常时才执行的代码块。 尽管“ catch”子句是可选的,但没有一个子句就无法真正处理异常。 这是因为“ catch”子句阻止了异常在调用堆栈中传播,从而使程序得以恢复。 如果“ try”块中发生异常,则控制权立即传递给“ catch”子句。 发生的异常也将传递到“ catch”块进行处理。 以下示例显示了如何使用“ catch”子句来处理“ ReferenceError”。 请注意,“ catch”子句中的“ ReferenceError”对象可通过“ exception”变量获得。

try {
  foo++;  // ReferenceError
} catch (exception) {
  var message = exception.message;

  // handle the exception
}

复杂的应用程序可能会生成各种异常。 在这种情况下,可以使用“ instanceof”运算符来区分各种类型的异常。 在下面的示例中,假定“ try”子句可以生成几种类型的异常。 相应的“ catch”子句使用“ instanceof”与所有其他类型的错误分开处理“ TypeError”和“ ReferenceError”异常。

try {
  // assume an exception occurs
} catch (exception) {
  if (exception instanceof TypeError) {
    // Handle TypeError exceptions
  } else if (exception instanceof ReferenceError) {
    // Handle ReferenceError exceptions
  } else {
    // Handle all other types of exceptions
  }
}

“最终”条款

“ try…catch…finally”语句的最后一个组成部分是可选的“ finally”子句。 “ finally”子句是在“ try”和“ catch”子句之后执行的代码块,无论是否有任何错误。 “ finally”子句对于包含无论如何都需要执行的清理代码(关闭文件等)很有用。 请注意,即使发生未捕获的异常,“ finally”子句甚至会执行。 在这种情况下,将执行“ finally”子句,然后抛出的异常将正常进行。

关于“ finally”子句的一个有趣的注释是,即使“ try”或“ catch”子句执行了“ return”语句,也将执行它。 例如,以下函数返回false,因为“ finally”子句是最后执行的东西。

function foo() {
  try {
    return true;
  } finally {
    return false;
  }
}

抛出异常

JavaScript允许程序员通过适当命名的“ throw”语句引发自己的异常。 对于没有经验的开发人员,此概念可能会有些困惑。 毕竟,开发人员努力编写没有错误的代码,但是“ throw”语句是有意引入的。 但是,故意抛出异常实际上可以导致更易于调试和维护的代码。 例如,通过创建有意义的错误消息,可以更轻松地识别和解决问题。

下面显示了“ throw”语句的几个示例。 对于可以作为异常抛出的数据类型没有任何限制。 捕获和抛出相同数据的次数也没有限制。 换句话说,可以引发,捕获然后再次引发异常。

throw true;
throw 5;
throw "error message";
throw null;
throw undefined;
throw {};
throw new SyntaxError("useful error message");

尽管“ throw”语句可以与任何数据类型一起使用,但是使用内置的异常类型有某些好处。 例如,Firefox通过添加调试信息(例如发生异常的文件名和行号)来对这些对象进行特殊处理。

作为一个示例场景,假定除法操作发生在应用程序中的某处。 由于除零的可能性,所以除法可能很麻烦。 在JavaScript中,这样的操作会导致“ NaN”。 这会导致难以调试的结果混乱。 如果应用程序大声抱怨除数为零,事情会简单得多。 以下“ if”语句通过抛出异常为我们完成了此任务。

if (denominator === 0)
  throw new Error("Attempted division by zero!");

当然,如下所示使用“ RangeError”可能更合适。

if (denominator === 0)
  throw new RangeError("Attempted division by zero!");

自定义异常对象

我们刚刚学习了如何使用内置的异常类型生成自定义的错误消息。 但是,另一种方法是通过扩展现有的“错误”类型来创建新的异常类型。 由于新类型继承自“ Error”,因此可以像其他内置异常类型一样使用它。 尽管JavaScript的继承主题不在本文讨论范围之内,但此处介绍了一种简单的技术。

下面的示例返回到除以零的问题。 与其像我们之前那样使用“ Error”或“ RangeError”对象,我们不如创建自己的异常类型。 在此示例中,我们将创建“ DivisionByZeroError”异常类型。 示例中的函数充当我们新类型的构造函数。 构造函数负责分配“ name”和“ message”属性。 该示例的最后两行使新类型继承自“ Error”对象。

function DivisionByZeroError(message) {
  this.name = "DivisionByZeroError";
  this.message = (message || "");
}

DivisionByZeroError.prototype = new Error();
DivisionByZeroError.prototype.constructor = DivisionByZeroError;

要记住的事情

  • “ try…catch…finally”语句用于处理异常。
  • “ try”子句标识可能产生异常的代码。
  • 仅在发生异常时才执行“ catch”子句。
  • 无论如何,始终执行“ finally”子句。
  • “ throw”语句用于生成异常。
  • 自定义异常对象应继承自现有的“错误”类型。

图片来自Fotolia

From: https://www.sitepoint.com/exceptional-exception-handling-in-javascript/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值