第 14 章:异常处理
目录
购买本书
(广告,请不要屏蔽。)

第 14 章:异常处理

本章介绍 JavaScript 的异常处理机制。首先,我们将概述什么是异常处理。

什么是异常处理?

在异常处理中,您通常会将紧密耦合的语句分组。 如果在执行这些语句时,其中一个语句导致错误,则继续执行其余语句毫无意义。相反,您应该尝试尽可能优雅地从错误中恢复。这有点类似于事务(但不具有原子性)。

让我们看看没有异常处理的代码

function processFiles() {
    var fileNames = collectFileNames();
    var entries = extractAllEntries(fileNames);
    processEntries(entries);
}
function extractAllEntries(fileNames) {
    var allEntries = new Entries();
    fileNames.forEach(function (fileName) {
        var entry = extractOneEntry(fileName);
        allEntries.add(entry);  // (1)
    });
}
function extractOneEntry(fileName) {
    var file = openFile(fileName);  // (2)
    ...
}
...

在 (2) 处,对 openFile() 中的错误做出反应的最佳方法是什么?显然,不应再执行语句 (1)。但我们也不想中止 extractAllEntries()。相反,跳过当前文件并继续处理下一个文件就足够了。为此,我们将异常处理添加到前面的代码中

function extractAllEntries(fileNames) {
    var allEntries = new Entries();
    fileNames.forEach(function (fileName) {
        try {
            var entry = extractOneEntry(fileName);
            allEntries.add(entry);
        } catch (exception) {  // (2)
            errorLog.log('Error in '+fileName, exception);
        }
    });
}
function extractOneEntry(fileName) {
    var file = openFile(fileName);
    ...
}
function openFile(fileName) {
    if (!exists(fileName)) {
        throw new Error('Could not find file '+fileName); // (1)
    }
    ...
}

异常处理有两个方面

  1. 如果出现无法在其发生位置进行有效处理的问题,则抛出异常。
  2. 找到可以处理错误的位置:捕获异常。

在 (1) 处,以下结构处于活动状态

    processFile()
        extractAllEntries(...)
            fileNames.forEach(...)
                function (fileName) { ... }
                    try { ... } catch (exception) { ... }
                        extractOneEntry(...)
                            openFile(...)

(1) 处的 throw 语句会向上遍历该树,并离开所有结构,直到遇到活动的 try 语句。然后,它会调用该语句的 catch 块,并将异常值传递给它。

JavaScript 中的异常处理

JavaScript 中的异常处理机制与大多数编程语言类似:try 语句将语句分组,并允许您拦截这些语句中的异常。

throw

throw 的语法如下:

throw «value»;

可以抛出任何 JavaScript 值。为简单起见,许多 JavaScript 程序只抛出字符串

// Don't do this
if (somethingBadHappened) {
    throw 'Something bad happened';
}

不要这样做。JavaScript 具有用于异常对象的特殊构造函数(请参阅错误构造函数)。使用这些构造函数或对其进行子类化(请参阅第 28 章)。它们的优点是 JavaScript 会自动添加堆栈跟踪(在大多数引擎上),并且它们有空间容纳其他特定于上下文的属性。最简单的解决方案是使用内置构造函数 Error()

if (somethingBadHappened) {
    throw new Error('Something bad happened');
}

try-catch-finally

try-catch-finally 的语法如下所示。try 是必需的,并且 catchfinally 中至少有一个也必须存在:

try {
    «try_statements»
}
catch («exceptionVar») {
   «catch_statements»
}
finally {
   «finally_statements»
}

其工作原理如下

示例

可以抛出任何值

function throwIt(exception) {
    try {
        throw exception;
    } catch (e) {
        console.log('Caught: '+e);
    }
}

以下是交互过程

> throwIt(3);
Caught: 3
> throwIt('hello');
Caught: hello
> throwIt(new Error('An error happened'));
Caught: Error: An error happened

finally 始终会被执行

function throwsError() {
    throw new Error('Sorry...');
}
function cleansUp() {
    try {
        throwsError();
    } finally {
        console.log('Performing clean-up');
    }
}

以下是交互过程

> cleansUp();
Performing clean-up
Error: Sorry...

finallyreturn 语句之后执行:

function idLog(x) {
    try {
        console.log(x);
        return 'result';
    } finally {
        console.log("FINALLY");
    }
}

以下是交互过程

> idLog('arg')
arg
FINALLY
'result'

返回值在执行 finally 之前排队

var count = 0;
function countUp() {
    try {
        return count;
    } finally {
        count++;  // (1)
    }
}

在执行语句 (1) 时,count 的值已经排队等待返回

> countUp()
0
> count
1

错误构造函数

ECMAScript 对以下错误构造函数进行了标准化。描述摘自 ECMAScript 5 规范:

以下是错误的属性

message
错误消息。
name
错误的名称。
stack
堆栈跟踪。这是非标准的,但在许多平台上都可用,例如 Chrome、Node.js 和 Firefox。

堆栈跟踪

错误的常见来源是外部错误(输入错误、文件丢失等)或内部错误(程序中的错误)。特别是在后一种情况下,您会遇到意外的异常,并且需要进行调试。通常,您没有运行调试器。对于“手动”调试,有两条信息很有用:

  1. 数据:变量的值是什么?
  2. 执行:异常发生在哪一行,以及哪些函数调用处于活动状态?

您可以将第一项(数据)的一些内容放入异常对象的 message 或属性中。第二项(执行)在许多 JavaScript 引擎上都通过堆栈跟踪得到支持,堆栈跟踪是创建异常对象时调用堆栈的快照。以下示例打印堆栈跟踪:

function catchIt() {
    try {
        throwIt();
    } catch (e) {
        console.log(e.stack); // print stack trace
    }
}
function throwIt() {
    throw new Error('');
}

以下是交互过程

> catchIt()
Error
    at throwIt (~/examples/throwcatch.js:9:11)
    at catchIt (~/examples/throwcatch.js:3:9)
    at repl:1:5
下一页:15. 函数