错误处理try catch

image.png

捕获异步

  1. setTimeout(() => {
  2. try{
  3. // do some
  4. } catch (err) {
  5. // 这里捕获
  6. }
  7. }, 1000)

抛出自定义错误throw

  1. let json = '{ "age": 30 }'; // 不完整的数据
  2. try {
  3. let user = JSON.parse(json); // <-- 没有 error
  4. if (!user.name) {
  5. throw new SyntaxError("Incomplete data: no name"); // (*)
  6. }
  7. alert( user.name );
  8. } catch(e) {
  9. alert( "JSON Error: " + e.message ); // JSON Error: Incomplete data: no name
  10. }

再次抛出

预料之外的错误,catch中不止如处理,则可以再次抛出。
image.png
此时,更外层的 try catch 将捕获到错误,但如果外部不存在这种结构,脚本就会被杀死。

try catch finally

image.png

* finally和return

控制转向外部之前 finally 先执行

  1. function func() {
  2. try {
  3. return 1;
  4. } catch (e) {
  5. //...
  6. } finally {
  7. alert('finally')
  8. }
  9. }
  10. alert(func()); // 先执行finally中的alert,再执行alert 1

全局catch

  1. window.onerror = function(message, url, line, col, error) {}
  2. // node
  3. process.on("uncaughtException")

自定义Error

扩展自Error

Error类的内部实现大致是:

  1. class Error {
  2. constructor(message) {
  3. this.message = message
  4. this.name = 'Error' // 不同Error类,比如SyntaxError就是this.name = 'SyntaxError'
  5. this.stack = <call stack> // 调用栈信息
  6. }
  7. }

所以我们可以继承,重写name。

  1. class MyError extends Error {
  2. constructor(message) {
  3. super(message)
  4. this.name = 'MyError'
  5. }
  6. }
  7. const myErr = new MyError('我的自定义错误')
  8. myErr.name = 'MyError'

通过 intanceof 操作符,可以判断错误是否是你的自定义错误,从而来做特殊的处理。如果不确定的错误,则应再次抛出。

  1. if(err instanceof MyError) {
  2. // ... do some
  3. } else {
  4. // 再次抛出,在try catch里说过。
  5. throw err
  6. }

深入继承

这里举个例子,访问对象的某个属性的错误处理。

  1. class ValidationError extends Error {
  2. constructor(msg) {
  3. super(msg)
  4. this.name = 'validationError'
  5. }
  6. }
  7. class PropertyRequiredError extends ValidatationError {
  8. constructor(property) {
  9. // 使用方便,只需要new PropertyRequiredError('属性名')即可,报错message由super,也就是Error的constructor函数来做this.message = message
  10. super('no property: ' + property)
  11. this.name = 'PropertyRequiredError'
  12. }
  13. }
  14. // read some json
  15. if(!user.name) {
  16. throw new PropertyRequiredError('name')
  17. }

我们在每个自定义的Error类中都重写了name,这步可以简写为

  1. // 一个自定义的Error父类,其他自定义Error类继承自该父类。
  2. // 因为我们无法重写Error内建类
  3. class MyError extends Error {
  4. constructor(msg) {
  5. super(msg)
  6. // 使用当前调用构造函数名
  7. this.name = this.constructor.name
  8. }
  9. }
  10. class ValidationError extends MyError {}
  11. new ValidationError() // this.name = ValidationError