如何在 JavaScript 中创建自定义错误?

IT技术 javascript exception
2021-01-28 19:57:10

出于某种原因,看起来构造函数委托在以下代码段中不起作用:

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

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

运行这个给The message is: ''. 关于原因的任何想法,或者是否有更好的方法来创建新的Error子类?applyError我不知道的本机构造函数进行ing有问题吗?

6个回答

更新您的代码以将您的原型分配给 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"}; 

根据评论编辑

在查看评论并试图记住为什么我将原型分配给Error.prototype而不是new Error()像 Nicholas Zakas 在他的文章中所做的那样之后,我使用以下代码创建了一个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()是创建的行号,而不是throw e发生的位置。但是,这可能比具有NotImplementedError.prototype.name = "NotImplementedError"影响 Error 对象的副作用更好

另外,请注意NotImplementedError2,当我没有.name明确设置时,它等于“错误”。但是,正如评论中提到的,因为该版本将原型设置为new Error(),所以我可以设置NotImplementedError2.prototype.name = "NotImplementedError2"并确定。

获得有意义的堆栈跟踪的简单技巧是在构造函数中生成错误并保存它的堆栈。它将为构造函数提供适当的调用堆栈 + 1 行(这是一个合适的回报):this.stack = new Error().stack;
2021-03-18 19:57:10
我仍然有点迷失在所有这些原型事物中。为什么在您的示例中,您将名称分配给 this.name 而不是 NotImplementedError.prototype.name?你能回答吗,这对我的理解很重要:)
2021-03-19 19:57:10
最佳答案,但Error.prototype直接服用可能是不好的形式。如果您以后想添加一个NotImplementedError.prototype.toString对象现在别名到Error.prototype.toString- 最好这样做NotImplementedError.prototype = new Error()
2021-03-24 19:57:10
根据code.google.com/p/chromium/issues/detail?id=228909 subclass.prototype = new Error()是错误的形式。你应该subclass.prototype = Object.create(superclass.prototype)改用。我希望它也可以解决堆栈跟踪问题。
2021-04-01 19:57:10
-1; 这是错误的。这样做NotImplementedError.prototype = Error.prototype;不会使instanceof治疗NotImplementedError作为一个子类Error,这让instanceof他们享受作为完全相同的类。如果您将上述代码粘贴到您的控制台中并尝试new Error() instanceof NotImplementedError您会得到true,这显然是错误的。
2021-04-03 19:57:10

以上所有答案都非常糟糕 - 真的。即使是107个​​ups!真正的答案在这里,伙计们:

从 Error 对象继承 - message 属性在哪里?

特尔;博士:

答:原因message是不是被集是Error是返回一个新的Error对象和它的功能操作this以任何方式。

B. 正确的做法是从构造函数返回apply的结果,并以通常复杂的javascripty方式设置原型:

function MyError() {
    var temp = Error.apply(this, arguments);
    temp.name = this.name = 'MyError';
    this.message = temp.message;
    if(Object.defineProperty) {
        // getter for more optimizy goodness
        /*this.stack = */Object.defineProperty(this, 'stack', { 
            get: function() {
                return temp.stack
            },
            configurable: true // so you can change it if you want
        })
    } else {
        this.stack = temp.stack
    }
}
//inherit prototype using ECMAScript 5 (IE 9+)
MyError.prototype = Object.create(Error.prototype, {
    constructor: {
        value: MyError,
        writable: true,
        configurable: true
    }
});

var myError = new MyError("message");
console.log("The message is: '" + myError.message + "'"); // The message is: 'message'
console.log(myError instanceof Error); // true
console.log(myError instanceof MyError); // true
console.log(myError.toString()); // MyError: message
console.log(myError.stack); // MyError: message \n 
// <stack trace ...>


 
//for EMCAScript 4 or ealier (IE 8 or ealier), inherit prototype this way instead of above code:
/*
var IntermediateInheritor = function() {};
IntermediateInheritor.prototype = Error.prototype;
MyError.prototype = new IntermediateInheritor();
*/

您可能会做一些技巧来枚举tmpError 的所有不可枚举属性来设置它们,而不是仅显式设置stackand message,但是 ie<9 不支持这种技巧

@MattKantor 或许可以回答这个问题?我想我最喜欢你的。
2021-03-14 19:57:10
取而代之的是temp.name = this.name = 'MyError',您可以这样做temp.name = this.name = this.constructor.name这样它也适用于的子类MyError
2021-03-14 19:57:10
此解决方案也适用于使用现有错误实例化自定义错误。如果您使用第三方库并希望使用您自己的自定义类型包装现有错误,则其他方法无法正常工作。仅供参考,您可以通过向它们传递现有错误来实例化普通错误。
2021-03-29 19:57:10
你不应该return this在构造函数中。
2021-04-06 19:57:10
我稍微简化和改进了这个方法:jsbin.com/rolojuhuya/1/edit?js,console
2021-04-08 19:57:10

在 ES2015 中,您可以使用class干净地执行此操作:

class NotImplemented extends Error {
  constructor(message = "", ...args) {
    super(message, ...args);
    this.message = message + " has not yet been implemented.";
  }
}

这不会修改全局Error原型,允许自定义messagename、 等属性,并正确捕获堆栈。它也很可读。

当然,您可能需要使用一种工具,例如babel您的代码是否会在旧浏览器上运行。

请注意,如果您使用babel它有不能扩展内置Error类的限制:babeljs.io/docs/en/caveats/#classes
2021-03-15 19:57:10
有关扩展错误的更多详细信息,请查看此处的自定义错误developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
2021-03-26 19:57:10

如果有人对如何创建自定义错误获取堆栈跟踪感到好奇

function CustomError(message) {
  this.name = 'CustomError';
  this.message = message || '';
  var error = new Error(this.message);
  error.name = this.name;
  this.stack = error.stack;
}
CustomError.prototype = Object.create(Error.prototype);

try {
  throw new CustomError('foobar');
}
catch (e) {
  console.log('name:', e.name);
  console.log('message:', e.message);
  console.log('stack:', e.stack);
}

标准的这一部分可以解释为什么Error.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 = ''
@Dave 我可能误解了这里的目的,但是您的NotImplementedError实现不应该返回returned变量吗?
2021-03-21 19:57:10
如何使用自定义错误类来模拟?例如,我的自定义错误类如何既用作创建实例的函数又用作构造函数?
2021-03-29 19:57:10
@BT 新实例上的 msg 属性如何影响thisin上的 msg 属性Error.apply(this, arguments);我是说这里对 Error 的调用是构造一个新对象,该对象被丢弃;不初始化分配给的已构造对象nie
2021-03-30 19:57:10
不,这不是真的。如果它返回一个新的 Error 实例,那么他的 msg 属性将起作用。
2021-03-31 19:57:10
@BT 我添加了一些示例代码,希望能更清楚地说明我想说的内容。
2021-04-08 19:57:10