“Script error.”是一个常见错误,但由于该错误不提供完整的报错信息(错误堆栈)
“Script error.”的产生原因
“Script error.”有时也被称为跨域错误。当网站请求并执行一个托管在第三方域名下的脚本时,就可能遇到该错误。最常见的情形是使用CDN托管JS资源。
为了更好地理解,在本地启动一个服务 假设以下HTML页面部署在http://10.133.47.91:6500
<!doctype html>
<html>
<head>
<title>js 错误测试</title>
</head>
<body>
<script src="http://10.133.47.91:6500/demo.js"></script>
<script>
window.onerror = function (message, url, line, column, error) {
console.log(message, url, line, column, error);
}
test(); // 调用demo.js中定义的test方法。
</script>
</body>
</html>
// http://10.133.47.91:6500/demo.js
function test() {
demo(); // ReferenceError: demo is not a function
}
Script error. 0 0 null
其实这并不是一个JavaScript Bug。出于安全考虑,浏览器会刻意隐藏其他域的JS文件抛出的具体错误信息,这样做可以有效避免敏感信息无意中被不受控制的第三方脚本捕获。因此,浏览器只允许同域下的脚本捕获具体错误信息,而其他脚本只知道发生了一个错误,但无法获知错误的具体内容。更多信息,请参见[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;
}
解法1:开启跨域资源共享CORS(Cross Origin Resource Sharing)
为了跨域捕获JavaScript异常,可执行以下两个步骤:
注意(对script标签直接添加crossorigin="anonymous"
不起作用)
- 添加跨域HTTP响应头。(必须)
说明** 大部分主流CDN默认添加了Access-Control-Allow-Origin
属性
Access-Control-Allow-Origin: *
例如:本地demo服务用的是express
app.all('*', function(req, res, next) {
res.header("Access-Control-Allow-Credentials", true)
res.header("Access-Control-Allow-Origin", "*")
res.header("Access-Control-Allow-Headers", "X-Requested-With")
res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS")
res.header("X-Powered-By", ' 3.2.1')
res.header("Content-Type", "application/json;charset=utf-8")
next();
});
- 添加
crossorigin="anonymous"
属性。
<script src="http://10.133.47.91:6500/demo.js" crossorigin="anonymous"></script>
此步骤的作用是告知浏览器以匿名方式获取目标脚本。这意味着请求脚本时不会向服务端发送潜在的用户身份信息(例如Cookies、HTTP证书等)。
- 完成上述两步之后,即可通过
window.onerror
捕获跨域脚本的报错信息。回到之前的案例,页面重新运行后,捕获到的结果是:
Uncaught ReferenceError: demo is not defined http://10.133.47.91:6500/demo.js 11 3
(可选)解法2:try catch
难以在HTTP请求响应头中添加跨域属性时,还可以考虑try catch
这个备选方案。
在之前的示例HTML页面中加入try catch
:
<!doctype html>
<html>
<head>
<title>js 错误测试</title>
</head>
<body>
<script
src="http://10.133.47.91:6500/demo.js"
></script>
<script>
window.onerror = function (message, url, line, column, error) {
console.log(message, url, line, column, error);
}
try {
test();
} catch (e) {
console.log(e);
throw e;
}
</script>
</body>
</html>
再次运行,输出结果如下:
ReferenceError: demo is not defined
at test (demo.js:11:3)
at (index):44:9