22.异常处理
原文: http://exploringjs.com/impatient-js/ch_exception-handling.html
本章介绍 JavaScript 如何处理异常。
暂且不说:JavaScript 直到 ES3 才支持异常。这就解释了为什么它们被语言及其标准库谨慎使用。
22.1。动机:抛出和捕捉异常
请考虑以下代码。它将存储在文件中的配置文件读入具有类Profile
实例的数组:
function readProfiles(filePaths) {
const profiles = [];
for (const filePath of filePaths) {
try {
const profile = readOneProfile(filePath);
profiles.push(profile);
} catch (err) { // (A)
console.log('Error in: '+filePath, err);
}
}
}
function readOneProfile(filePath) {
const profile = new Profile();
const file = openFile(filePath);
// ··· (Read the data in `file` into `profile`)
return profile;
}
function openFile(filePath) {
if (!fs.existsSync(filePath)) {
throw new Error('Could not find file '+filePath); // (B)
}
// ··· (Open the file whose path is `filePath`)
}
让我们来看看 B 行发生了什么:发生错误,但处理问题的最佳位置不是当前位置,而是 A 行。在那里,我们可以跳过当前文件并转到下一个文件。
因此:
- 在 B 行中,我们使用
throw
语句来指示存在问题。 - 在 A 行中,我们使用
try-catch
语句来处理问题。
当我们抛出时,以下构造是活动的:
readProfiles(···)
for (const filePath of filePaths)
try
readOneProfile(···)
openFile(···)
if (!fs.existsSync(filePath))
throw
throw
走上这个构造链,直到找到try
语句。在try
语句的catch
子句中继续执行。
22.2。 throw
throw «value»;
可以抛出任何值,但最好抛出Error
的实例:
throw new Error('Problem!');
22.2.1。用于创建错误对象的选项
使用类
Error
。这在 JavaScript 中比在更静态的语言中更少限制,因为您可以将自己的属性添加到实例:const err = new Error('Could not find the file'); err.filePath = filePath; throw err;
使用
Error
的 JavaScript 子类之一(后面列出)。子类
Error
自己。class MyError extends Error { } function func() { throw new MyError; } assert.throws( () => func(), MyError);
### 22.3。 `try-catch-finally`
`try`语句的最大版本如下所示:
```js
try {
// try_statements
} catch (error) {
// catch_statements
} finally {
// finally_statements
}
try
子句是必需的,但您可以省略catch
或finally
(但不能同时省略)。从 ECMAScript 2019 开始,如果您对抛出的值不感兴趣,也可以省略(error)
。
22.3.1。 catch
条款
如果在try
块中抛出异常(并且之前未捕获),则将其分配给catch
子句的参数,并执行该子句中的代码。除非它被指向其他地方(通过return
或类似),否则在catch
子句之后继续执行:使用finally
子句 - 如果存在 - 或在try
语句之后。
以下代码演示了行 A 中抛出的值确实在行 B 中捕获。
const errorObject = new Error();
function func() {
throw errorObject; // (A)
}
try {
func();
} catch (err) { // (B)
assert.equal(err, errorObject);
}
22.3.2。 finally
条款
让我们看一下finally
的一个常见用例:你已经创建了一个资源,并希望在你完成它时总是销毁它 - 无论在使用它时发生了什么。你可以按如下方式实现:
const resource = createResource();
try {
// Work with `resource`: errors may be thrown.
} finally {
resource.destroy();
}
始终执行finally
- 即使抛出错误(行 A):
let finallyWasExecuted = false;
assert.throws(
() => {
try {
throw new Error(); // (A)
} finally {
finallyWasExecuted = true;
}
},
Error
);
assert.equal(finallyWasExecuted, true);
始终执行finally
- 即使有return
语句(行 A):
let finallyWasExecuted = false;
function func() {
try {
return; // (A)
} finally {
finallyWasExecuted = true;
}
}
func();
assert.equal(finallyWasExecuted, true);
22.4。错误类及其属性
引用 ECMAScript 规范:
Error
[根类]RangeError
:表示不在允许值的设置或范围内的值。ReferenceError
:表示检测到无效的参考值。SyntaxError
:表示发生了解析错误。TypeError
:用于表示当其他 NativeError 对象都不是故障原因的适当指示时不成功的操作。URIError
:表示其中一个全局 URI 处理函数的使用方式与其定义不兼容。
22.4.1。错误类的属性
考虑err
,Error
的一个实例:
const err = new Error('Hello!');
assert.equal(String(err), 'Error: Hello!');
err
的两个属性特别有用:
.message
:仅包含错误消息。assert.equal(err.message, 'Hello!');
.stack
:包含堆栈跟踪。它受到所有主流浏览器的支持。assert.equal( err.stack, ` Error: Hello! at Context.<anonymous> (ch_exception-handling.js:1:13) `.trim());
exercises/exception-handling/call_function_test.js
参见测验应用程序。