script error.的定义
script error.
是一个常见的错误,它类似于b is not defined
,script error.
与这个错误不同的点就在于其并没有详细的错误信息
script error.产生原因
先来说一下b is not defined
这个错误要如何产生
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
window.onerror = function (message, url, line, column, error) {
console.log(message, url, line, column, error);
}
console.log(b);
</script>
</body>
</html>

结果很明显的,我们捕获到了这个错误的详细信息
下面来复现下script error.
,我们将上面的一小段代码上传到服务器上,然后在现有的js中加上这样一段代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
window.onerror = function (message, url, line, column, error) {
console.log(message, url, line, column, error);
}
</script>
<script src="https://cmc-web.oss-cn-beijing.aliyuncs.com/2.js"></script>
</body>
</html>

你看,这个神秘的script error.
出现了,而且最有用的信息恐怕就是script error.
了????
这两种case
有何不同?第二种情况下,我们引用了一个非同源的文件。
在webkit中,有这样一段代码
bool ScriptExecutionContext::sanitizeScriptError(String& errorMessage, int& lineNumber, String& sourceURL)
{
KURL targetURL = completeURL(sourceURL);
if (securityOrigin()->canRequest(targetURL))
return false;
errorMessage = "Script error.";
sourceURL = String();
lineNumber = 0;
return true;
}
它会解析来源url,如果和当前url不是同源的时候,就会强制errorMessage
为script error.
。所以,可以得出结论:这是浏览器出于安全的考虑,刻意隐藏了其他域下js文件抛出的错误信息,这样可以避免敏感信息无意中被不受控制的第三方脚本捕获。所以,对于其他域下发生的错误,我们也只是能知道这里有一个错误,并无从知道错误信息的详细内容。
script error.解决办法
不知道详细错误信息可不行呀,那怎么办呢?这里有两种解决方案
先来试试crossOrigin?
这个其实就是开启CORS(跨域资源共享)
,这个方法要分两步来操作:
1、script
标签加上crossOrigin
属性:告知浏览器以匿名的方式获取目标脚本,这意味着请求脚本时不会向服务端发送潜在的用户身份信息
crossorigin="anonymous"
2、添加跨域HTTP
响应头
Access-Control-Allow-Origin: *
或者
Access-Control-Allow-Origin: https://cmc-web.oss-cn-beijing.aliyuncs.com
完成这两步操作后,看下结果:

再来试试try catch?
先来说下try catch
的优点:浏览器不会对其异常进行跨域拦截,所以这个时候可以拿到堆栈信息。
但同时它也有缺点:
如果使用try catch
的话,只能捕获到同步的错误,像这样:
try {
console.log(b);
} catch (error) {
console.log(error);
}

如果换成异步呢
try {
setTimeout(() => {
console.log(b);
}, 2000)
} catch (error) {
console.log(error);
}

会发现并拿不到错误信息,所以针对此现象,我们对不同的动作做不同的改动
拦截普通函数捕获异常
考虑到这句console.log(b)
直接写在代码里,也不会打包成功,所以这块我们稍加修改,重新上传到服务器
function test() {
console.log(b);
}
下面写一个拦截函数,考虑到我们可能对很多个函数都要做拦截,所以采取这样一个思想:函数执行统一交给另外一个函数,统一拦截这个函数
function emit(callback) {
callback()
}
let originError = emit;
emit = function(func) {
const addStack = new Error(`Event error`).stack;
const wrapppedFunc = function(...args) {
try {
return func.apply(this, args);
}
catch (err) {
// 异常发生时,扩展堆栈
err.stack += '\n' + addStack;
throw err;
}
}
return originError.call(this, wrapppedFunc);
}

拦截事件函数捕获异常
<button id="btn">点我</button>
// script
let oBtn = document.getElementById('btn');
oBtn.addEventListener('click', function() {
test();
}, false)

因为上面写了对于普通函数的异常捕获拦截,所以可以这样解:
let oBtn = document.getElementById('btn');
oBtn.addEventListener('click', function() {
emit(test);
}, false)

如果不想每次都调用emit
函数,就要实现一个对于事件拦截的函数:
let originAddEventListener = EventTarget.prototype.addEventListener;
EventTarget.prototype.addEventListener = function(type, func, async) {
const addStack = new Error(`Event ${type}`).stack;
const wrapppedFunc = function(...args) {
try {
return func.apply(this, args);
}
catch (err) {
// 异常发生时,扩展堆栈
err.stack += '\n' + addStack;
throw err;
}
}
return originAddEventListener.call(this, type, wrapppedFunc, async);
}
结果如下:

拦截setTimeout函数捕获异常
setTimeout
同上
let originSetTimeout = window.setTimeout;
window.setTimeout = function(func, delay) {
const addStack = new Error(`setTimout error`).stack;
const wrapppedFunc = function(...args) {
try {
return func(args);
}
catch (err) {
// 异常发生时,扩展堆栈
err.stack += '\n' + addStack;
throw err;
}
}
return originSetTimeout.call(this, wrapppedFunc, delay);
}
综上几个case
,发现都是用try catch
包裹,那有没有办法使用一个通用的办法通通解决呢?
写在最后
本文理了一下script error
从产生到解决的过程,我们可以思考一下最后抛出来的问题~
最后,分享一下我的个人微信公众号「web前端日记」,大家可以关注一波~
