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

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

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

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


当前回答

上面的很多方法都不起作用。

最后一个是一个实际的错误。如果您使用字符串,它看起来很好,但它不会提供堆栈跟踪。如果你抛出错误,你不能有“Uncaught BadError: bad”,所以你必须删除自定义错误(遗憾的是)。如果你抛出一个对象,它看起来有点偏离,最后一个只是一个平均错误。

此方法创建一个具有自定义名称的错误,同时保留堆栈跟踪:

var errProto = Object.create(Error.prototype, { constructor: { value: Error, enumerable: false, writable: true, configurable: true } }) var isFirefox = !!window.InstallTrigger // Hide stack for Firefox only, as stacks can cause problems with high "frame" counts. function createError(name, message, hideStack) { if (message == null) { message = "" } var customError = Error(message) customError.name = name Object.setPrototypeOf(customError, errProto) if (isFirefox && hideStack) { customError.stack = "" } else if (isFirefox) { var stack = customError.stack var newline = stack.indexOf("\n") + 1 stack = stack.slice(newline) customError.stack = stack var split = stack.split(":") if (split.length > 4) { var a = split[3] var b = split[4] var t = b.slice(0, b.indexOf("\n")) customError.lineNumber = Number(a) customError.columnNumber = Number(t) } } else { var stack = customError.stack var split = stack.split("\n") var secondPart = split.slice(2).join("\n") stack = split[0] + "\n" + secondPart customError.stack = stack var split = secondPart.split(":") var a = split[2] var b = split[3] } throw customError } var frame = 0 function aFunction() { if (++frame === 100) { createError("LazyError", "function getting lazy", false, true) } else { requestAnimationFrame(aFunction) } } setTimeout(aFunction, Math.random() * 500) * { font-family: Verdana; } Check your inspector!

其他回答

class NotImplementedError extends Error {
  constructor(message) {
    super(message);
    this.message = message;
  }
}
NotImplementedError.prototype.name = 'NotImplementedError';
module.exports = NotImplementedError;

and

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);
}

它只是这个答案的一个类表示。

输出

NotImplementedError: NotImplementedError message
  ...stacktrace
ex1 instanceof NotImplementedError = true
ex1 instanceof Error = true
ex1.name = NotImplementedError
ex1.message = NotImplementedError message

根据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

以下是Mozilla官方文档中的错误。

function NotImplementedError(message) {
    var instance = new Error(message);
    instance.name = 'NotImplementedError';

    Object.setPrototypeOf(instance, Object.getPrototypeOf(this));
    if (Error.captureStackTrace) {
        Error.captureStackTrace(instance, NotImplementedError);
    }
    return instance;
}

NotImplementedError.prototype = Object.create(Error.prototype, {
    constructor: {
        value: Error,
        enumerable: false,
        writable: true,
        configurable: true
    }
});

构造函数需要类似于工厂方法,并返回您想要的内容。如果你需要额外的方法/属性,你可以在返回之前将它们添加到对象中。

function NotImplementedError(message) { return new Error("Not implemented", message); }

x = new NotImplementedError();

不过我不明白你为什么要这么做。为什么不直接使用new Error…? 自定义异常在JavaScript(或任何非类型化语言)中并没有真正添加太多内容。

更新代码,将原型分配给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。