认识
一、 代码执行异常
1.1 错误类型
-
Error
: 最基本的错误类型,其他的错误类型都继承自该类型。通过Error
,我们可以自定义Error
类型。 -
RangeError
: 范围错误。当出现堆栈溢出(递归没有终止条件)、数值超出范围(new Array
传入负数或者一个特别大的整数)情况时会抛出这个异常。 -
ReferenceError
: 引用错误。当一个不存在的对象被引用时发生的异常。 -
SyntaxError
: 语法错误。如变量以数字开头;花括号没有闭合等。 -
TypeError
: 类型错误。如把number
当str
使用。 -
URIError
: 向全局URI
处理函数传递一个不合法的URI
时,就会抛出这个异常。如使用decodeURI('%')
、decodeURIComponent('%')
。 -
EvalError
: 一个关于eval
的异常,不会被javascript
抛出。
1.2 try catch
try{}catch(){}
能捕捉到的异常必须是线程执行已经进入 try catch
但 try catch
未执行完的时候抛出来的。当程序运行到 try catch
里面时,如果未报错,则忽略 catch
中的代码,若报错,则不执行 try
报错内容后面的代码,转而执行 catch
中的代码。try{}catch(){}
有如下特点:
-
无法捕获语法错误: 因为语法错误是在语法检查阶段就报错了,线程执行尚未进入
try catch
代码块,自然就无法捕获到异常。 -
无法捕获异步错误: 因为等异步函数里面的事件进入事件队列的时候,主线程已经离开了
try catch
,所以try catch
是无法捕获异步函数的错误的。 -
无法捕获
Promise
异常: 当异步函数抛出异常时,对于宏任务而言,执行函数时已经将该函数推入栈,此时并不在try-catch
所在的栈,所以try-catch
并不能捕获到错误。
语法
// 示例1:常规运行时错误,可以捕获 ✅
try {
let a = undefined;
if (a.length) {
console.log('111');
}
} catch (e) {
console.log('捕获到异常:', e);
}
// 示例2:语法错误,不能捕获 ❌
try {
const notdefined,
} catch(e) {
console.log('捕获不到异常:', 'Uncaught SyntaxError');
}
// 示例3:异步错误,不能捕获 ❌
try {
setTimeout(() => {
console.log(notdefined);
}, 0)
} catch(e) {
console.log('捕获不到异常:', 'Uncaught ReferenceError');
}
1.3 window.onerror
window.onerror
事件在冒泡阶段执行, 用于全局捕获错误。 window.onerror
得到了五个参数,获取的报错堆栈相对很完整。 window.onerror
可以捕获异步错误; 捕获不到静态资源加载异常的错误,原因是资源类型错误没有冒泡,只能在捕获阶段捕获,而 window.onerror
是通过在冒泡阶段捕获错误,对静态资源加载类型异常无效,所以只能借助 window.addEventListener('error', callback, true)
的方式捕获。
语法
window.onerror = function(message, source, lineno, colno, error) { ... }
-
message
:错误信息(字符串)。可用于HTMLonerror
=处理程序中的event
。 -
source
:发生错误的脚本URL
(字符串) -
lineno
:发生错误的行号(数字) -
colno
:发生错误的列号(数字) -
error
:Error
对象
示例
window.onerror = function(message, source, lineno, colno, error) {
console.log("捕获到的错误信息是:", message, source, lineno, colno, error);
};
// 示例1:常规运行时错误,可以捕获 ✅
console.log(notdefined);
// 示例2:语法错误,不能捕获 ❌
const notdefined;
// 示例3:异步错误,可以捕获 ✅
setTimeout(() => {
console.log(notdefined);
}, 0);
// 示例4:资源错误,不能捕获 ❌
let script = document.createElement("script");
script.type = "text/javascript";
script.src = "https://www.test.com/index.js";
document.body.appendChild(script);
1.4 window.addEventListener("error")
window.addEventListener('error', true)
用于在捕获阶段全局捕获错误。可以捕获异步错误、可以捕获网络异常、静态资源加载异常错误。但是通过 window.addEventListener('error', true)
获取的报错堆栈不是很完整。
window.addEventListener("error", function (event) {
const { error } = event;
console.log("error: ", error.message);
console.log("error: ", error.stack);
});
二、Promise 异常
2.1 promise().catch()
Promise.reject(); // Uncaught (in promise) undefined
2.2 window.onrejectionhandled
当Promise
被reject
并且错误信息已经被catch
处理的时候,这个错误不会被window.onerror
以及window.addEventListener("error",handler,true)
捕获。但是有专门的rejectionhandled
事件方法进行捕获处理
语法
Promise.reject(); // Uncaught (in promise) undefined
2.3 window.onunhandledrejection
当Promise
被reject
并且错误信息没有被catch
处理的时候,这个错误不会被window.onerror
以及window.addEventListener("error",handler,true)
捕获。但是有专门的unhandledrejection
事件方法进行捕获处理
语法
Promise.reject(); // Uncaught (in promise) undefined
2.4 window.addEventListener("rejectionhandled")
当Promise
被reject
并且错误信息已经被catch
处理的时候,这个错误不会被window.onerror
以及window.addEventListener("error",handler,true)
捕获。但是有专门的rejectionhandled
事件方法进行捕获处理
语法
window.addEventListener("rejectionhandled", event => {
console.log("Promise rejected; reason: " + event.reason);
}, false);
window.onrejectionhandled = ()=>{
}
Promise.reject(throw new Error("xxx")).catch(error=> { console.log(error) });
2.5 window.addEventListener("unhandledrejection")
当Promise
被reject
并且错误信息没有被catch
处理的时候,这个错误不会被window.onerror
以及window.addEventListener("error",handler,true)
捕获。但是有专门的unhandledrejection
事件方法进行捕获处理
语法
window.addEventListener("unhandledrejection", event => {
console.warn(`UNHANDLED PROMISE REJECTION: ${event.reason}`);
});
window.onunhandledrejection = event => {
console.warn(`UNHANDLED PROMISE REJECTION: ${event.reason}`);
};
Promise.reject(throw new Error("xxx"));
三、网络请求异常
在浏览器端发起一个接口请求时,如果请求的 url
的有问题,也会抛出异常。
3.1 xhr.onerror
如果是 xhr.send
方法执行时出现异常,可以通过 xhr.onerror
或者 xhr.addEventListener('error', callback)
的方式捕获异常。
xhr.open('get', '/user/userInfo');
xhr.send(); // send localhost:3000/user/userinfo net::ERR_FAILED
3.2 xhr.addEventListener('error',callback)
如果是 xhr.send
方法执行时出现异常,可以通过 xhr.onerror
或者 xhr.addEventListener('error', callback)
的方式捕获异常。
xhr.open('get', '/user/userInfo');
xhr.send(); // send localhost:3000/user/userinfo net::ERR_FAILED
3.3 window.onerror
如果是 xhr.open
方法执行时出现异常,可以通过 window.addEventListener('error', callback)
或者 window.onerror
的方式捕获异常。
xhr.open('GET', "https://") // Uncaught DOMException: Failed to execute 'open' on 'XMLHttpRequest': Invalid URL
at ....
3.4 window.addEventListener('error', callback)
如果是 xhr.open
方法执行时出现异常,可以通过 window.addEventListener('error', callback)
或者 window.onerror
的方式捕获异常。
xhr.open('GET', "https://") // Uncaught DOMException: Failed to execute 'open' on 'XMLHttpRequest': Invalid URL
at ....
3.5 fetch(url).then(callback).catch(callback)
接口调用是通过 fetch
发起的, 我们可以通过 fetch(url).then(callback).catch(callback)
的方式去捕获异常。
四、静态资源加载异常
有的时候,如果我们页面的img
、js
、css
等资源链接失效,就会提示资源类型加载如异常。
4.1 xxx.onerror
img.onerror = function(){};
link.onerror = function(){};
script.onerror = function(){};
4.2 window.addEventListener('error', callback, true)
针对这一类的异常,我们可以通过 window.addEventListener('error', callback, true)
的方式进行全局捕获。这里要注意一点,使用 window.onerror = callback
的方式是无法捕获静态资源类异常的。原因是资源类型错误没有冒泡,只能在捕获阶段捕获,而 window.onerror
是通过在冒泡阶段捕获错误,对静态资源加载类型异常无效,所以只能借助 window.addEventListener('error', callback, true)
的方式捕获。
<img src="localhost:3000/data.png" /> // Get localhost:3000/data.png net::ERR_FILE_NOT_FOUND
五、跨域脚本执行异常
当项目中引用的第三方脚本执行发生错误时,会抛出一类特殊的异常。这类型异常和我们刚才讲过的异常都不同,它的 msg
只有 Script error
信息,没有具体的行、列、类型信息。
之以会这样,是因为浏览器的安全机制: 浏览器只允许同域下的脚本捕获具体异常信息,跨域脚本中的异常,不会报告错误的细节,这样可以有效避免敏感信息无意中被第三方(不受控制的)脚本捕获到。
5.1 window.addEventListener('error', callback)
针对这类型的异常,我们可以通过 window.addEventListener('error', callback)
或者 window.onerror
的方式捕获异常。当然,如果我们想获取这类异常的详情,需要做以下两个操作:
- 在发起请求的
script
标签上添加crossorigin="anonymous"
<script src="http://xxxxxxxx/texterror.js" crossorigin="anonymous"></script>