Script error. 的产生原因和解决办法

886 阅读2分钟

“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" 不起作用)

  1. 添加跨域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();
});

  1. 添加crossorigin="anonymous"属性。
    <script src="http://10.133.47.91:6500/demo.js" crossorigin="anonymous"></script>

此步骤的作用是告知浏览器以匿名方式获取目标脚本。这意味着请求脚本时不会向服务端发送潜在的用户身份信息(例如Cookies、HTTP证书等)。

  1. 完成上述两步之后,即可通过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