错误处理,try...catch
try...catch 语法
try {
//代码
} catch (err) {
//错误补货
}
特点:
try...catch
是同步执行的,异步的错误是不能被捕获的。
try {
setTimeout(function () {
noSuchVariable; // 脚本将在这里停止运行
}, 1000);
} catch (err) {
alert("不工作");
}
Error 对象
try {
} catch (err) {}
对于所有内建的 error,error 对象具有两个主要属性:
name
Error 名称,例如对于未定义的变量,名称是"ReferenceError"
。
message
Error 详细文字描述。
stack
当前的调用栈:用于调试目的的一个字符串,其中包含有关导致 error 的嵌套调用序列的信息。
例如:
try {
lalala;
} catch (err) {
alert(err.name); // ReferenceError
alert(err.message); // lalala is not defined
alert(err.stack); // ReferenceError: lalala is not defined at (...call stack)
// 也可以将一个 error 作为整体显示出来
// error 信息被转换为像 "name: message" 这样的字符串
alert(err); // ReferenceError: lalala is not defined
}
自定义 error
let json = '{"age":30}';
try {
let user = JSON.parse(json);
console.log(user.name); //没有name
} catch (err) {
console.log("doesn't execute");
}
'throw'操作符
语法如下:
throw <error object>
在 JavaScript 中有很多内建的标准 error 的构建起:Error
、SyntaxError
、ReferenceError
、TypeError
等。
let error = new Error(message);
let error = new SyntaxError(message);
let error = new TypeError(message);
例如:
let error = new Error("Things happen o_O");
console.log(error.name); //Error
console.log(error.message); // Things happen o_O
let json = '{"age":30}';
try {
let user = JSON.parse(json);
if (!user.name) {
throw new SyntaxError("数据不全,缺少name属性");
}
console.log(user.name);
} catch (err) {
console.log("JSON Error: " + err.message); //JSON Error: 数据不全,缺少name属性
}
再次抛出
function readData() {
let json = '{ "age": 30 }';
try {
// ...
blabla(); // error!
} catch (err) {
// ...
if (!(err instanceof SyntaxError)) {
throw err; // 再次抛出(不知道如何处理它)
}
}
}
try {
readData();
} catch (err) {
alert("External catch got: " + err); // 捕获了它!
}
try...catch...finally
try{
...尝试执行的代码...
}catch(err){
...错误处理...
}finally{
...总会执行的代码...
}
finally 和 return
finally
子句适用于try...catch
的任何出口。 这包括显式的return
。
例如:
function f() {
try {
return 1;
} catch (err) {
} finally {
console.log("finally");
}
}
console.log(f()); //先执行finally 再return
全局 catch
window.onerror = function (message, url, line, col, error) {};
message
:错误信息(字符串)。url
:发生错误的脚本 URL(字符串)。line
:发生错误的行号(数字)。col
:发生错误的列号(数字)。error
:Error 对象(对象)。
全局错误处理程序的作用通常不是恢复脚本的执行---一般是用来将错误信息发送给开发者。
自定义 Error,扩展 Error
扩展 Error
需求: 检查 json 格式和数据完整性。
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = "ValidationError";
}
}
function test() {
throw new ValidationError("Whoops!");
}
try {
test();
} catch (err) {
console.log(err.name); //ValidationError
console.log(err.message); //Whoops!
console.log(err.stack); //ValidationError: Whoops! at test (<anonymous>:9:9) at <anonymous>:13:3
}
//完整版
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = "ValidationError";
}
}
function test(json) {
let user = JSON.parse(json);
if (!user.age) {
throw new ValidationError("缺少age属性");
}
if (!user.name) {
throw new ValidationError("缺少name属性");
}
return user;
}
try {
let user = readUser('{"age:25"}');
} catch (err) {
if (err instanceof ValidationError) {
console.log("数据格式错误:" + err.message);
} else if (err instanceof SyntaxError) {
console.log("JSON格式错误:" + err.message);
} else {
throw err;
}
}
深入继承
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = "ValidationError";
}
}
class PropertyRequiredError extends ValidationError {
constructor(property) {
super("No property: " + property);
this.name = "PropertyRequiredError";
this.property = property;
}
}
function readUser(json) {
let user = JSON.parse(json);
if (!user.age) {
throw new PropertyRequiredError("age");
}
if (!user.name) {
throw new PropertyRequiredError("name");
}
return user;
}
try {
let user = readUser('{"age":25}');
} catch (err) {
if (err instanceof ValidationError) {
console.log("Invalid data: " + err.message); //Invalid data: No property: name
console.log(err.name); //PropertyRequiredError
console.log(err.property); //name
} else if (err instanceof SyntaxError) {
console.log("JSON Syntax Error: " + err.message);
} else {
throw err;
}
}
class MyError extends Error {
constructor(message) {
super(message);
this.name = this.constructor.name;
}
}
class ValidationError extends MyError {}
class PropertyRequiredError extends ValidationError {
constructor(property) {
super("No property: " + property);
this.property = property;
}
}
// name 是对的
alert(new PropertyRequiredError("field").name); // PropertyRequiredError
包装异常
为了解决 在 readUser 函数中的多个 if 判断
- 创建一个新的类
ReadError
来表示一般的"数据读取"error。 - 函数
readUser
将补货内部发生的数据读取 error,例如ValidationError
和SyntaxError
,并生成一个ReadError
来进行替代。 - 对象
ReadError
会把对原始 error 的引用保存在其cause
属性中。
class ReadError extends Error {
constructor(message, cause) {
super(message);
this.cause = cause;
this.name = "ReadError";
}
}
class ValidationError extends Error {}
class PropertyRequiredError extends ValidationError {}
function validateUser(user) {
if (!user.age) {
throw new PropertyRequiredError("age");
}
if (!user.name) {
throw new PropertyRequiredError("name");
}
}
function readUser(json) {
let user;
try {
user = JSON.parse(json);
} catch (err) {
if (err instanceof SyntaxError) {
throw new ReadError("Syntax Error", err);
} else {
throw err;
}
}
try {
validateUser(user);
} catch (err) {
if (err instanceof ValidationError) {
throw new ReadError("Validation Error", err);
} else {
throw err;
}
}
}
try {
readUser("{bad json}");
} catch (e) {
if (e instanceof ReadError) {
console.log(e);
console.log("Original error: " + e.cause);
} else {
throw e;
}
}