为什么 Mocha/Chai 不将 babel 自定义错误与常规自定义错误一样对待?

Why don't Mocha/Chai treat babel custom errors the same as regular custom errors?

这适用于 Mocha/Chai:

describe('Chai throw() with old-style custom errors', ()=> {

  // old way
  function ES5Error(message = 'todo', value) {
    this.name = 'ES5Error';
    this.message = message;
  }
  ES5Error.prototype = new Error();

  it('catches ES5Errors', ()=> {
    var err = new ES5Error('This is a bad function.');
    var fn = function () { throw err; }
    expect(fn).to.throw(ES5Error);
    expect(fn).to.throw(Error);
    expect(fn).to.throw(/bad function/);
    expect(fn).to.not.throw('good function');
    expect(fn).to.throw(ES5Error, /bad function/);
    expect(fn).to.throw(err);
  });
});

虽然基于 class 的方法不会:

describe('Chai throw() with new-style custom errors', ()=> {
  // New way
  class ExtendError extends Error {
    constructor(message = 'todo', value) {
      super(message);
      this.name = 'ExtendError';
      this.message = message;
    }
  }

  it('catches ExtendError', ()=> {
    var err = new ExtendError('This is a bad function.');
    var fn = function () { throw err; }
    expect(fn).to.throw(ExtendError);
    expect(fn).to.throw(Error);
    expect(fn).to.throw(/bad function/);
    expect(fn).to.not.throw('good function');
    expect(fn).to.throw(ExtendError, /bad function/);
    expect(fn).to.throw(err);
  });
});

我也实现了相关的。虽然很有趣,但它仍然不适用于 mocha throws()。我基本上很乐意使用 ES5 风格的错误,但我只是不确定实际问题是什么。当我转译 ExtendError 的代码时,我无法立即看到任何会触发 expect 子句的内容:

'use strict';

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }

function _inherits(subClass, superClass) {
    if (typeof superClass !== "function" && superClass !== null) {
        throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
    }
    subClass.prototype = Object.create(superClass && superClass.prototype, {
        constructor: {
            value: subClass,
            enumerable: false,
            writable: true,
            configurable: true
        }
    });
    if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
}

var ExtendError = function (_Error) {
  _inherits(ExtendError, _Error);

  function ExtendError() {
    var message = arguments.length <= 0 || arguments[0] === undefined ? 'todo' : arguments[0];
    var value = arguments[1];

    _classCallCheck(this, ExtendError);

    var _this = _possibleConstructorReturn(this, _Error.call(this, message));

    _this.name = 'ExtendError';
    _this.message = message;
    return _this;
  }

  return ExtendError;
}(Error);

Mocha/Chai的问题是什么?

问题不在 Chai 身上。如果你用你的 ES6 实现 err instanceof ExtendError 你会得到 false!

ExtendError 你 运行 的 ES5 和 ES6 实现实际上是不同的。

  • 在 ES5 端,您调用 Error 但不对 return 值执行任何操作。这是正确的。

  • 在ES6端,super(...)调用转换为

    var _this = _possibleConstructorReturn(this, _Error.call(this, message));`
    

    然后_this取代了原来ES6代码中的thisError 的 return 值在 ES6 代码中使用,这是一切都变得糟糕的地方,因为您然后从构造函数中 return 的对象是一个 Error 对象但不是一个 ExtendError 对象。

我会继续使用 ES5 语法派生自 Error。我尝试了一些方法来保持 ES6 语法,但最终它们是混合的或者做了非常糟糕的事情。我想到的最不令人反感的方法是:

class ExtendError {
    constructor(message = 'todo', value) {
        Error.call(this, message);
        this.name = 'ExtendError';
        this.message = message;
    }
}

ExtendError.prototype = Object.create(Error.prototype);
ExtendError.prototype.constructor = ExtendError;

它没有很好地利用 ES6 class 糖...