异常捕获与抛出


上次更新时间:10/29/2020, 6:07:20 PM 0

# 1.try...catch

try...catch 语句用于包裹可能出现意外的语句,当出现异常时抛出,不影响下面函数的执行。

try语句包含了由一个或者多个语句组成的try块, 和至少一个catch块或者一个finally块的其中一个,或者两个兼有, 下面是三种形式的try声明:

  • try...catch
  • try...finally
  • try...catch...finally

finally 语句无论是否抛出异常都会执行

try {
  try {
    throw new Error("oops");
  }
  catch (ex) {
    console.error("inner", ex.message);
    throw ex;
  }
  finally {
    console.log("finally");
  }
}
catch (ex) {
  console.error("outer", ex.message);
}

// Output:
// "inner" "oops"
// "finally"
// "outer" "oops"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

参考:

try...catch

# 2.throw语句

throw语句用来抛出一个用户自定义的异常。当前函数的执行将被停止(throw之后的语句将不会执行),并且控制将被传递到调用堆栈中的第一个catch块。如果调用者函数中没有catch块,程序将会终止。

语法:throw expression;

  1. 抛出一个 error 对象
try {
   if (true) {
       throw new Error('错误')
   }
} catch (e) {
   console.log(e);
}
// Error: 错误
1
2
3
4
5
6
7
8
  1. 抛出一个指定对象
function UserException(message) {
   this.message = message;
   this.name = "UserException";
}
function getMonthName(mo) {
    mo = mo - 1;
    var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
        "Aug", "Sep", "Oct", "Nov", "Dec"];
    if (months[mo] !== undefined) {
        return months[mo];
    } else {
        throw new UserException("InvalidMonthNo");
    }
}

try {
   var myMonth = 1; // 15 超出边界并引发异常
   var monthName = getMonthName(myMonth);
} catch (e) {
   console.log(e.message, e.name);
}

// InvalidMonthNo UserException
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 3.Error构造器

通过Error的构造器可以创建一个错误对象。当运行时错误产生时,Error的实例对象会被抛出。Error对象也可用于用户自定义的异常的基础对象。

语法: new Error(描述信息, 所在文件名, 所在文件行号)

  1. 抛出一个基本错误
try {
    throw new Error("Whoops!");
} catch (e) {
    alert(e.name + ": " + e.message);
}
1
2
3
4
5
  1. 自定义异常类型
function MyError(message) {
  this.name = 'MyError';
  this.message = message || 'Default Message';
  this.stack = (new Error()).stack;
}
MyError.prototype = Object.create(Error.prototype);
MyError.prototype.constructor = MyError;

try {
  throw new MyError('custom message');
} catch (e) {
  console.log(e.name + ':' + e.message);
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 其他类型

除了基础的 Error, 还有6个其他类型错误构造函数:

  • EvalError:错误原因与 eval() 有关
  • InternalError:引擎内部抛出的实例,如"递归太多"
  • RangeError:数值变量或参数超出其有效范围
  • ReferenceError:无效引用
  • SyntaxError:语法错误
  • TypeError:变量或参数不属于有效类型
  • URIError:给 encodeURI()或 decodeURl()传递的参数无效

# 4.window.onerror

当JavaScript运行时错误(包括语法错误)发生时,window会触发一个ErrorEvent接口的error事件,并执行window.onerror()。

语法: window.onerror = function(message, source, lineno, colno, error) { ... }

函数参数:

  • message:错误信息(字符串)。可用于HTML onerror=""处理程序中的event。
  • source:发生错误的脚本URL(字符串)
  • lineno:发生错误的行号(数字)
  • colno:发生错误的列号(数字)
  • error:Error对象(对象)

若该函数返回true,则阻止执行默认事件处理函数。

window.onerror = function(message, source, lineno, colno, error) {
    console.log('捕获到异常:',{message, source, lineno, colno, error});
}
1
2
3

# 5.window.addEventListener('error')

当一项资源(如<img>或<script>)加载失败,加载资源的元素会触发一个Event接口的error事件,并执行该元素上的onerror()处理函数。这些error事件不会向上冒泡到window,不过(至少在Firefox中)能被单一的window.addEventListener捕获

语法:window.addEventListener('error', function(event) { ... })

window.addEventListener('error', function(event) { 
    console.log('捕获到异常:' + event)
}
1
2
3

# 6.window.addEventListener('unhandledrejection')

Promisereject 且没有被catch时候,会触发unhandledrejection事件;这可能发生在 window 下,但也可能发生在 Worker 中。

  1. 基本的异常上报
window.addEventListener("unhandledrejection", event => {
  console.warn(`UNHANDLED PROMISE REJECTION: ${event.reason}`);
});
1
2
3
  1. 阻止默认处理,添加自定义处理
window.addEventListener('unhandledrejection', function (event) {
  // ...您的代码可以处理未处理的拒绝...

  // 防止默认处理(例如将错误输出到控制台)
  event.preventDefault();
});
1
2
3
4
5
6

# 7.监控页面奔溃

当页面奔溃,js都无法运行时,可以使用 window 对象的 load 和 beforeunload 实现监控

window.addEventListener('load', function () {
    sessionStorage.setItem('good_exit', 'pending');
    setInterval(function () {
        sessionStorage.setItem('time_before_crash', new Date().toString());
    }, 1000);
});

window.addEventListener('beforeunload', function () {
    sessionStorage.setItem('good_exit', 'true');
});

if(sessionStorage.getItem('good_exit') && sessionStorage.getIte('good_exit') !== 'true') {
    /*
    insert crash logging code here
    */
    alert('Hey, welcome back from your crash, looks like you crashed on: ' + sessionStorage.getItem('time_before_crash'));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 8.解决Script Error跨域

添加crossOrigin属性可以解决Script Error跨域问题。cors 设置属性介绍

<script src="http://xxx.com/test.js" crossorigin></script>
1

# 9.Vue异常捕捉-errorHandler

errorHandler 指定组件的渲染和观察期间未捕获错误的处理函数。这个处理函数被调用时,可获取错误信息和 Vue 实例。

Vue.config.errorHandler = function (err, vm, info) {
  // handle error
  // `info` 是 Vue 特定的错误信息,比如错误所在的生命周期钩子
  // 只在 2.2.0+ 可用
}
1
2
3
4
5

文档介绍

# 10.总结

  • 可疑区域增加: try...catch
  • 全局监控JS异常: window.onerror
  • 全局监控静态资源异常: window.addEventListener
  • 全局捕获没有 catch 的 promise 异常:unhandledrejection
  • 监控网页崩溃:window 对象的 load 和 beforeunload
  • Script Error跨域: 添加 crossOrigin 属性
  • VUE 异常捕获:errorHandler
  • React 异常捕获:componentDidCatch
上次更新时间: 10/29/2020, 6:07:20 PM