前言
关于前端异常处理,东西不多也不复杂都是老生常谈。但是纸上得来终觉浅,要知此事须躬行。来,请随我一起看下去 (ง •_•)ง。
关于常见错误
首先通过一行代码查看浏览器下都有哪些错误类型
Object.getOwnPropertyNames(window).filter(err => err.endsWith ('Error'))
可以得到所有的Error类型。
对于我们开发常见的有以下几个
- RangeError
- ReferenceError
- SyntaxError(一般不需要我们处理)
- TypeError
1:RangeError
RangeError对象标明一个错误,当一个值不在其所允许的范围或者集合中。
var arr = []
a.length = -1 // Uncaught RangeError: Invalid array length
2:ReferenceError
ReferenceError(引用错误)对象代表当一个不存在的变量被引用时发生的错误。
1:变量没有被声明
b.length // Uncaught ReferenceError: b is not defined
2:错误的作用域
function numbers () {
var num1 = 2,
num2 = 3;
return num1 + num2;
}
console.log(num1); // ReferenceError num1 is not defined.
3:SyntaxError
SyntaxError 对象代表尝试解析语法上不合法的代码的错误。
var a b c // Uncaught SyntaxError: Unexpected identifier
对于SyntaxError错误在我们编写代码阶段就已经被抛出,不需要我们再做代码处理
4:TypeError
TypeError(类型错误)对象用来表示值的类型非预期类型时发生的错误。
// undefined and null cases on which the substring method won't work
var foo = undefined;
foo.substring(1); // TypeError: foo is undefined
var foo = null;
foo.substring(1); // TypeError: foo is null
// Certain methods might require a specific type
var foo = {}
Symbol.keyFor(foo); // TypeError: foo is not a symbol
var foo = 'bar'
Object.create(foo); // TypeError: "foo" is not an object or null
以上我们罗列出了代码中的常见错误问题,接下去我们探讨一下如何捕获问题
关于错误捕获
1:老生常谈的 try catch
先看例子
// 同步错误
try {
var arr = []
arr.length = -1
} catch (err) {
console.log('err', err) // err RangeError: Invalid array length
}
try {
b.length
} catch (err) {
console.log('err', err) // err ReferenceError: b is not defined
}
try {
var foo = undefined;
foo.substring(1);
} catch (err) {
console.log('err', err) // err TypeError: Cannot read properties of undefined (reading 'substring')
}
console.log('start')
try {
console.log('run')
b.length
} catch (err) {
console.log('err', err)
}
console.log('end')
start
run
err ReferenceError: b is not defined
end
try块里的同步错误都被正确捕获并且不影响代码正常执行
// 异步错误
var a = null
console.log('start')
try {
setTimeout(() => {
console.log('我是一个异步错误:', a.splice(','))
});
} catch (err) {
console.log('err', err)
}
console.log('end')
看来异步是搞不定了!!!
结论1:try块可以捕获同步错误且不影响代码正常执行流,但异步无能为力
2:window.onerror
// 异步错误
/**
* @param {String} msg 错误信息
* @param {String} url 出错文件
* @param {Number} row 行号
* @param {Number} col 列号
* @param {Object} error 错误详细信息
*/
console.log('start')
setTimeout(() => {
let a = null
console.log('我是一个异步错误:', a.splice(','))
});
console.log('end')
window.onerror = function (msg, url, row, col, error) {
console.log('我捕获了一个异步错误', {
msg, url, row, col, error
})
return true;
};
运行上面的代码我们可以看出window.onerror貌似可以捕捉到异步错误,而且报错信息很全
// 来同步也试一个
let a = null
window.onerror = function (msg, url, row, col, error) {
console.log('我捕获一个同步错误:', {
msg, url, row, col, error
})
return true;
};
console.log('我是一个同步错误', a.splice(','))
从上方可以看出window.onerror只能捕获异步错误,同步错误无能为力
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
</body>
<script type="text/javascript">
window.onerror = function(msg, url, row, col, error) {
console.log('我捕获一个引用资源错误:', {
msg, url, row, col, error
})
return true
};
</script>
<script src="./outSideSource.js"></script>
</html>
// outSideSource.js
var arr = []
arr.length = -1
从上方可以看出
window.onerror可以捕获一个引用资源的报错,但如果是一个异步的资源可以吗?
// outSideSource.js
setTimeout(() => {
var arr = []
arr.length = -1
}, 5000)
假设我们引用的资源不存在是否会被捕获?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
</body>
<script type="text/javascript">
window.onerror = function(msg, url, row, col, error) {
console.log('我捕获一个引用资源错误:', {
msg, url, row, col, error
})
return true
};
</script>
<script src="./abcd.js"></script>
</html>
可以看到对于不存在的资源确实无能为力
结论2:window.onerror可以捕获异步错误,引用资源内部的错误。但同步的无能为力,资源不存在也无能为力。
3:window.addEventListener
具体参见MDN
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
</body>
<script type="text/javascript">
window.addEventListener('error', (error) => { console.log('我捕获一个引用资源错误:', error); }, true)
</script>
<script src="./abcd.js"></script>
</html>
可以看出对于资源不存在的情况也可以捕获
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<img src="./abcd.js" />
</body>
<script type="text/javascript">
window.addEventListener('error', (error) => {
console.log('我捕获一个引用资源错误:', error);
}, true)
</script>
</html>
以上可以看出Dom图片资源错误也可以捕捉,非常棒!
window.addEventListener("unhandledrejection", function(e){
e.preventDefault()
console.log('我知道 promise 的错误了:', e.reason);
return true;
});
Promise.reject('promise error1');
new Promise((resolve, reject) => {
reject('promise error2');
});
new Promise((resolve) => {
resolve();
}).then(() => {
throw 'promise error3'
});
以上可以看出unhandledrejection并不能完全捕获reject
window.addEventListener("unhandledrejection", function(e){
e.preventDefault()
console.log('我知道 promise 的错误了:', e.reason);
return true;
});
Promise.reject('promise error1').catch((e) => console.log(e));
new Promise((resolve, reject) => {
reject('promise error2');
}).catch((e) => console.log(e));
new Promise((resolve) => {
resolve();
}).then(() => {
throw 'promise error3'
});
结论3:
window.addEventListener这个api很是强大,只有promise的部分reject不能正确捕获。那么我们要如何处理?其实在我们实际开发中直接使用promise用的比较少,多是用async await来做异步任务,所以如果代码中出现了promise建议还是需要写上catch进行捕获,虽然确实是麻烦一些。
4:cdn引用问题
先看看MDN的解释
这一块,嗯还没想好如何处理,暂且先做记录吧
总结
| 捕获方法 | 同步 | 异步 | 引用资源 | 资源不存在 | Dom相关错误 | promise |
|---|---|---|---|---|---|---|
try_catch | ✔ | ❌ | ❌ | ❌ | ❌ | ❌ |
window.onerror | ❌ | ✔ | ✔ | ❌ | ❌ | ❌ |
window.addEventListener_error | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ |
window.addEventListener_unhandledrejection | ❌ | ❌ | ❌ | ❌ | ❌ | ✔(部分) |