出于某种原因,在下面的代码段中,构造函数委托似乎不起作用:

function NotImplementedError() { 
  Error.apply(this, arguments); 
}
NotImplementedError.prototype = new Error();

var nie = new NotImplementedError("some message");
console.log("The message is: '"+nie.message+"'")

运行该命令得到的消息是:"。有什么想法,为什么,或者是否有更好的方法来创建一个新的错误子类?是否有一个问题,应用到本机错误构造函数,我不知道?


当前回答

我只需要实现这样的东西,发现堆栈在我自己的错误实现中丢失了。我要做的是创建一个虚拟错误,并从中检索堆栈:

My.Error = function (message, innerException) {
    var err = new Error();
    this.stack = err.stack; // IMPORTANT!
    this.name = "Error";
    this.message = message;
    this.innerException = innerException;
}
My.Error.prototype = new Error();
My.Error.prototype.constructor = My.Error;
My.Error.prototype.toString = function (includeStackTrace) {
    var msg = this.message;
    var e = this.innerException;
    while (e) {
        msg += " The details are:\n" + e.message;
        e = e.innerException;
    }
    if (includeStackTrace) {
        msg += "\n\nStack Trace:\n\n" + this.stack;
    }
    return msg;
}

其他回答

function InvalidValueError(value, type) {
    this.message = "Expected `" + type.name + "`: " + value;
    var error = new Error(this.message);
    this.stack = error.stack;
}
InvalidValueError.prototype = new Error();
InvalidValueError.prototype.name = InvalidValueError.name;
InvalidValueError.prototype.constructor = InvalidValueError;

更新代码,将原型分配给Error。Prototype和instanceof以及你的断言工作。

function NotImplementedError(message = "") {
    this.name = "NotImplementedError";
    this.message = message;
}
NotImplementedError.prototype = Error.prototype;

不过,我会抛出你自己的对象并检查name属性。

throw {name : "NotImplementedError", message : "too lazy to implement"}; 

根据评论进行编辑

在看了评论并试图记住为什么我将prototype分配给Error后。而不是像Nicholas Zakas在他的文章中所做的new Error(),我用下面的代码创建了一个jsFiddle:

function NotImplementedError(message = "") { this.name = "NotImplementedError"; this.message = message; } NotImplementedError.prototype = Error.prototype; function NotImplementedError2(message = "") { this.message = message; } NotImplementedError2.prototype = new Error(); try { var e = new NotImplementedError("NotImplementedError message"); throw e; } catch (ex1) { console.log(ex1.stack); console.log("ex1 instanceof NotImplementedError = " + (ex1 instanceof NotImplementedError)); console.log("ex1 instanceof Error = " + (ex1 instanceof Error)); console.log("ex1.name = " + ex1.name); console.log("ex1.message = " + ex1.message); } try { var e = new NotImplementedError2("NotImplementedError2 message"); throw e; } catch (ex1) { console.log(ex1.stack); console.log("ex1 instanceof NotImplementedError2 = " + (ex1 instanceof NotImplementedError2)); console.log("ex1 instanceof Error = " + (ex1 instanceof Error)); console.log("ex1.name = " + ex1.name); console.log("ex1.message = " + ex1.message); }

控制台输出如下。

undefined
ex1 instanceof NotImplementedError = true
ex1 instanceof Error = true
ex1.name = NotImplementedError
ex1.message = NotImplementedError message
Error
    at window.onload (http://fiddle.jshell.net/MwMEJ/show/:29:34)
ex1 instanceof NotImplementedError2 = true
ex1 instanceof Error = true
ex1.name = Error
ex1.message = NotImplementedError2 message

这证实了我遇到的“问题”是错误的堆栈属性是创建new error()的行号,而不是抛出e发生的地方。然而,这可能比NotImplementedError.prototype.name = "NotImplementedError"行影响Error对象的副作用要好。

另外,注意NotImplementedError2,当我没有显式地设置.name时,它等于“Error”。然而,正如评论中提到的,因为该版本将prototype设置为new Error(),所以我可以设置NotImplementedError2.prototype.name = "NotImplementedError2"并OK。

这是我的实现:

class HttpError extends Error {
  constructor(message, code = null, status = null, stack = null, name = null) {
    super();
    this.message = message;
    this.status = 500;

    this.name = name || this.constructor.name;
    this.code = code || `E_${this.name.toUpperCase()}`;
    this.stack = stack || null;
  }

  static fromObject(error) {
    if (error instanceof HttpError) {
      return error;
    }
    else {
      const { message, code, status, stack } = error;
      return new ServerError(message, code, status, stack, error.constructor.name);
    }
  }

  expose() {
    if (this instanceof ClientError) {
      return { ...this };
    }
    else {
      return {
        name: this.name,
        code: this.code,
        status: this.status,
      }
    }
  }
}

class ServerError extends HttpError {}

class ClientError extends HttpError { }

class IncorrectCredentials extends ClientError {
  constructor(...args) {
    super(...args);
    this.status = 400;
  }
}

class ResourceNotFound extends ClientError {
  constructor(...args) {
    super(...args);
    this.status = 404;
  }
}

示例用法#1:

app.use((req, res, next) => {
  try {
    invalidFunction();
  }
  catch (err) {
    const error = HttpError.fromObject(err);
    return res.status(error.status).send(error.expose());
  }
});

示例用法#2:

router.post('/api/auth', async (req, res) => {
  try {
    const isLogged = await User.logIn(req.body.username, req.body.password);

    if (!isLogged) {
      throw new IncorrectCredentials('Incorrect username or password');
    }
    else {
      return res.status(200).send({
        token,
      });
    }
  }
  catch (err) {
    const error = HttpError.fromObject(err);
    return res.status(error.status).send(error.expose());
  }
});

根据Joyent的说法,你不应该乱搞堆栈属性(我在这里给出的很多答案中看到了),因为它会对性能产生负面影响。他们是这么说的:

Stack:一般来说,不要搞砸这个。甚至不要扩大它。V8只在有人实际读取属性时才计算它,这极大地提高了可处理错误的性能。如果读取属性只是为了扩充它,即使调用者不需要堆栈,最终也会付出代价。

我喜欢并想要提到他们的想法,包装原始错误,这是一个很好的替代传递堆栈。

下面是我如何创建一个自定义错误,考虑到上面提到的:

es5版本:

function RError(options) { options = options || {}; // eslint-disable-line no-param-reassign this.name = options.name; this.message = options.message; this.cause = options.cause; // capture stack (this property is supposed to be treated as private) this._err = new Error(); // create an iterable chain this.chain = this.cause ? [this].concat(this.cause.chain) : [this]; } RError.prototype = Object.create(Error.prototype, { constructor: { value: RError, writable: true, configurable: true } }); Object.defineProperty(RError.prototype, 'stack', { get: function stack() { return this.name + ': ' + this.message + '\n' + this._err.stack.split('\n').slice(2).join('\n'); } }); Object.defineProperty(RError.prototype, 'why', { get: function why() { var _why = this.name + ': ' + this.message; for (var i = 1; i < this.chain.length; i++) { var e = this.chain[i]; _why += ' <- ' + e.name + ': ' + e.message; } return _why; } }); // usage function fail() { throw new RError({ name: 'BAR', message: 'I messed up.' }); } function failFurther() { try { fail(); } catch (err) { throw new RError({ name: 'FOO', message: 'Something went wrong.', cause: err }); } } try { failFurther(); } catch (err) { console.error(err.why); console.error(err.stack); console.error(err.cause.stack); }

es6版本:

class RError extends Error { constructor({name, message, cause}) { super(); this.name = name; this.message = message; this.cause = cause; } [Symbol.iterator]() { let current = this; let done = false; const iterator = { next() { const val = current; if (done) { return { value: val, done: true }; } current = current.cause; if (!val.cause) { done = true; } return { value: val, done: false }; } }; return iterator; } get why() { let _why = ''; for (const e of this) { _why += `${_why.length ? ' <- ' : ''}${e.name}: ${e.message}`; } return _why; } } // usage function fail() { throw new RError({ name: 'BAR', message: 'I messed up.' }); } function failFurther() { try { fail(); } catch (err) { throw new RError({ name: 'FOO', message: 'Something went wrong.', cause: err }); } } try { failFurther(); } catch (err) { console.error(err.why); console.error(err.stack); console.error(err.cause.stack); }

我已经把我的解决方案放到了一个模块中,这里是:https://www.npmjs.com/package/rerror

这在Cesium DeveloperError中很好地实现了:

文档 源

它的简化形式是:

var NotImplementedError = function(message) {
    this.name = 'NotImplementedError';
    this.message = message;
    this.stack = (new Error()).stack;
}

// Later on...

throw new NotImplementedError();