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

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

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

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


当前回答

以不能使用instanceof为代价,下面的代码保留了原始的堆栈跟踪,并且没有使用任何非标准的技巧。

// the function itself
var fixError = function(err, name) {
    err.name = name;
    return err;
}

// using the function
try {
    throw fixError(new Error('custom error message'), 'CustomError');
} catch (e) {
    if (e.name == 'CustomError')
        console.log('Wee! Custom Error! Msg:', e.message);
    else
        throw e; // unhandled. let it propagate upwards the call stack
}

其他回答

这部分的标准可以解释为什么错误。Apply调用不会初始化对象:

15.11.1错误构造函数作为函数调用 当Error作为函数而不是构造函数调用时,它会创建和 初始化一个新的Error对象。因此函数调用Error(…)是 等价于对象创建表达式new Error(… 相同的参数。

在这种情况下,Error函数可能确定它没有作为构造函数被调用,因此它返回一个新的Error实例,而不是初始化this对象。

用下面的代码进行测试似乎证明了这就是实际发生的情况:

function NotImplementedError() { 
   var returned = Error.apply(this, arguments);
   console.log("returned.message = '" + returned.message + "'");
   console.log("this.message = '" + this.message + "'");
}
NotImplementedError.prototype = new Error();

var nie = new NotImplementedError("some message");

运行此命令时输出如下:

returned.message = 'some message'
this.message = ''

MDN有一个很好的例子:

try {
  throw new Error('Whoops!');
} catch (e) {
  console.log(e.name + ': ' + e.message);
}
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。

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