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

时间:2022-02-28 01:28:09

For some reason it looks like constructor delegation doesn't work in the following snippet:

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

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

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

Running this gives The message is: ''. Any ideas as to why, or if there is a better way to create a new Error subclass? Is there a problem with applying to the native Error constructor that I don't know about?

运行它给出的信息是:“。关于为什么,或者是否有更好的方法来创建新的错误子类?应用到我不知道的本地错误构造函数有什么问题吗?

18 个解决方案

#1


172  

Update your code to assign your prototype to the Error.prototype and the instanceof and your asserts work.

更新代码以将原型分配给错误。原型和instanceof和你的断言工作。

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

However, I would just throw your own object and just check the name property.

但是,我只会抛出您自己的对象,并检查name属性。

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

Edit based on comments

编辑根据评论

After looking at the comments and trying to remember why I would assign prototype to Error.prototype instead of new Error() like Nicholas Zakas did in his article, I created a jsFiddle with the code below:

在看完评论并试着记住为什么我会把原型分配给错误。原型而不是新的错误()像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);
}

The console output was this.

控制台输出就是这个。

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

This confirmes the "problem" I ran into was the stack property of the error was the line number where new Error() was created, and not where the throw e occurred. However, that may be better that having the side effect of a NotImplementedError.prototype.name = "NotImplementedError" line affecting the Error object.

这证实了我遇到的“问题”是错误的堆栈属性是创建新错误()的行号,而不是抛出e发生的位置。但是,如果使用NotImplementedError.prototype.name = "NotImplementedError"行来影响错误对象,可能会更好。

Also, notice with NotImplementedError2, when I don't set the .name explicitly, it is equal to "Error". However, as mentioned in the comments, because that version sets prototype to new Error(), I could set NotImplementedError2.prototype.name = "NotImplementedError2" and be OK.

另外,请注意NotImplementedError2,当我没有显式地设置.name时,它等于“Error”。但是,正如在评论中提到的,因为那个版本将prototype设置为新的错误(),所以我可以设置NotImplementedError2.prototype.name = "NotImplementedError2",然后就可以了。

#2


81  

All of the above answers are terrible awful - really. Even the one with 107 ups! The real answer is here guys:

以上所有的答案都非常糟糕——真的。就是那个有107个包裹的人!真正的答案是:

Inheriting from the Error object - where is the message property?

从错误对象继承——消息属性在哪里?

TL;DR:

TL;博士:

A. The reason message isn't being set is that Error is a function that returns a new Error object and does not manipulate this in any way.

答:没有设置消息的原因是,错误是一个返回一个新的错误对象的函数,它不会以任何方式操作它。

B. The way to do this right is to return the result of the apply from the constructor, as well as setting the prototype in the usual complicated javascripty way:

这样做的方法是返回应用程序的结果,并在通常复杂的javascript方法中设置原型:

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();
*/

You could probably do some trickery to enumerate through all the non-enumerable properties of the tmp Error to set them rather than explicitly setting only stack and message, but the trickery isn't supported in ie<9

您可能会通过一些技巧来枚举tmp错误的所有不可枚举属性来设置它们,而不是显式地设置堆栈和消息,但是在ie<9中不支持欺骗。

#3


18  

If anyone is curious on how to create a custom error and get the stack trace:

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

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

#4


8  

In ES2015, you can use class to do this cleanly:

在2015年,您可以使用类来进行清洁:

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

This does not modify the global Error prototype, allows you to customize message, name, and other attributes, and properly captures the stack. It's also pretty readable.

这不会修改全局错误原型,允许您自定义消息、名称和其他属性,并正确地捕获堆栈。这也是非常可读的。

Of course, you may need to use a tool like babel if your code will be running on older browsers.

当然,如果您的代码在旧的浏览器上运行,您可能需要使用像babel这样的工具。

#5


7  

This section of the standard may explain why the Error.apply call doesn't initialize the object:

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

15.11.1 The Error Constructor Called as a Function

15.11.1错误构造函数称为函数。

When Error is called as a function rather than as a constructor, it creates and initialises a new Error object. Thus the function call Error(...) is equivalent to the object creation expression new Error(...) with the same arguments.

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

In this case the Error function probably determines that it's not being called as a constructor, so it returns a new Error instance rather than initializing the this object.

在这种情况下,错误函数可能决定了它不被称为构造函数,因此它返回一个新的错误实例,而不是初始化这个对象。

Testing with the following code seems to demonstrate that this is in fact what's happening:

用下面的代码进行测试似乎表明,这实际上是正在发生的事情:

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

The following output is generated when this is run:

在运行此操作时生成以下输出:

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

#6


6  

I had a similar issue to this. My error needs to be an instanceof both Error and NotImplemented, and it also needs to produce a coherent backtrace in the console.

我也有类似的问题。我的错误需要是一个错误和NotImplemented的实例,并且它还需要在控制台中产生一个一致的回溯。

My solution:

我的解决方案:

var NotImplemented = (function() {
  var NotImplemented, err;
  NotImplemented = (function() {
    function NotImplemented(message) {
      var err;
      err = new Error(message);
      err.name = "NotImplemented";
      this.message = err.message;
      if (err.stack) this.stack = err.stack;
    }
    return NotImplemented;
  })();
  err = new Error();
  err.name = "NotImplemented";
  NotImplemented.prototype = err;

  return NotImplemented;
}).call(this);

// TEST:
console.log("instanceof Error: " + (new NotImplemented() instanceof Error));
console.log("instanceof NotImplemented: " + (new NotImplemented() instanceofNotImplemented));
console.log("message: "+(new NotImplemented('I was too busy').message));
throw new NotImplemented("just didn't feel like it");

Result of running with node.js:

以node.js运行的结果:

instanceof Error: true
instanceof NotImplemented: true
message: I was too busy

/private/tmp/t.js:24
throw new NotImplemented("just didn't feel like it");
      ^
NotImplemented: just didn't feel like it
    at Error.NotImplemented (/Users/colin/projects/gems/jax/t.js:6:13)
    at Object.<anonymous> (/Users/colin/projects/gems/jax/t.js:24:7)
    at Module._compile (module.js:449:26)
    at Object.Module._extensions..js (module.js:467:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Module.runMain (module.js:487:10)
    at process.startup.processNextTick.process._tickCallback (node.js:244:9)

The error passes all 3 of my criteria, and although the stack property is nonstandard, it is supported in most newer browsers which is acceptable in my case.

这个错误传递了我的全部3个标准,尽管堆栈属性是非标准的,但在大多数更新的浏览器中都支持它,这在我的例子中是可以接受的。

#7


6  

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;

#8


3  

Accoring to Joyent you shouldn’t mess with the stack property (which I see in lots of answers given here), because it will have a negative impact on performance. Here is what they say:

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

stack: generally, don't mess with this. Don't even augment it. V8 only computes it if someone actually reads the property, which improves performance dramatically for handlable errors. If you read the property just to augment it, you'll end up paying the cost even if your caller doesn't need the stack.

stack:一般来说,不要搞砸了。甚至不增加。V8只有在有人真正读取该属性时才计算它,这将极大地提高可操作性错误的性能。如果您读取该属性只是为了增加它,那么即使您的调用者不需要堆栈,您也会付出代价。

I like and would like to mention their idea of wrapping the original error which is a nice replacement for passing on the stack.

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

So here is how I create a custom error, considering the above mentioned:

这里是我创建一个自定义错误的方法,考虑到上面提到的:

es5 version:

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 version:

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

I’ve put my solution into a module, here it is: https://www.npmjs.com/package/rerror

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

#9


2  

I just had to implement something like this and found that the stack was lost in my own error implementation. What I had to do was create a dummy error and retrieve the stack from that:

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

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

#10


2  

I used the Constructor Pattern to create the new error object. I defined the prototype chain such as an Error instance. See the MDN Error constructor reference.

我使用构造函数模式来创建新的错误对象。我定义了原型链,比如一个错误实例。请参阅MDN错误构造函数引用。

You can check this snippet on this gist.

您可以在这个要点上查看这个片段。

IMPLEMENTATION

// Creates user-defined exceptions
var CustomError = (function() {
  'use strict';

  //constructor
  function CustomError() {
    //enforces 'new' instance
    if (!(this instanceof CustomError)) {
      return new CustomError(arguments);
    }
    var error,
      //handles the arguments object when is passed by enforcing a 'new' instance
      args = Array.apply(null, typeof arguments[0] === 'object' ? arguments[0] : arguments),
      message = args.shift() || 'An exception has occurred';

    //builds the message with multiple arguments
    if (~message.indexOf('}')) {
      args.forEach(function(arg, i) {
        message = message.replace(RegExp('\\{' + i + '}', 'g'), arg);
      });
    }

    //gets the exception stack
    error = new Error(message);
    //access to CustomError.prototype.name
    error.name = this.name;

    //set the properties of the instance
    //in order to resemble an Error instance
    Object.defineProperties(this, {
      stack: {
        enumerable: false,
        get: function() { return error.stack; }
      },
      message: {
        enumerable: false,
        value: message
      }
    });
  }

  // Creates the prototype and prevents the direct reference to Error.prototype;
  // Not used new Error() here because an exception would be raised here,
  // but we need to raise the exception when CustomError instance is created.
  CustomError.prototype = Object.create(Error.prototype, {
    //fixes the link to the constructor (ES5)
    constructor: setDescriptor(CustomError),
    name: setDescriptor('JSU Error')
  });

  function setDescriptor(value) {
    return {
      configurable: false,
      enumerable: false,
      writable: false,
      value: value
    };
  }

  //returns the constructor
  return CustomError;
}());

USAGE

The CustomError constructor can receive many arguments to build the message, e.g.

CustomError构造函数可以接收许多参数来构建消息。

var err1 = new CustomError("The url of file is required"),
    err2 = new CustomError("Invalid Date: {0}", +"date"),
    err3 = new CustomError("The length must be greater than {0}", 4),
    err4 = new CustomError("Properties .{0} and .{1} don't exist", "p1", "p2");

throw err4;

And this is how the custom error looks:

这就是自定义错误的样子:

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

#11


1  

The constructor needs to be like a factory method and return what you want. If you need additional methods/properties, you can add them to the object before returning it.

构造函数需要像工厂方法一样,并返回您想要的。如果您需要其他方法/属性,您可以在返回之前将它们添加到对象中。

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

x = new NotImplementedError();

Though I'm not sure why you'd need to do this. Why not just use new Error... ? Custom exceptions don't really add much in JavaScript (or probably any untyped language).

虽然我不知道你为什么要这么做。为什么不使用新的错误…吗?自定义异常在JavaScript(或任何非类型化语言)中并没有真正的增加。

#12


1  

This is implemented nicely in the Cesium DeveloperError:

这在铯开发者的错误中很好地实现了:

In it's simplified form:

在简化的形式:

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

// Later on...

throw new NotImplementedError();

#13


0  

At the expense of not being able to use instanceof, the following preserves the original stack trace and doesn't use any non-standard tricks.

在不能够使用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
}

#14


0  

Another alternative , might not work in all enviroments.Atleast assured it works in nodejs 0.8 This approach uses a non standard way of modifying the internal proto prop

另一种选择,可能在所有环境中都不起作用。至少保证它在nodejs 0.8中工作,该方法使用一种非标准的方法来修改内部proto支持。

function myError(msg){ 
      var e = new Error(msg); 
      _this = this; 
      _this.__proto__.__proto__ = e;
}

#15


0  

If you are using Node/Chrome. The following snippet will get you extension which meets the following requirements.

如果您使用的是节点/Chrome。下面的代码片段将得到满足以下需求的扩展。

  • err instanceof Error
  • 犯错instanceof错误
  • err instanceof CustomErrorType
  • 犯错instanceof CustomErrorType
  • console.log() returns [CustomErrorType] when created with a message
  • 当创建消息时,console.log()返回[CustomErrorType]。
  • console.log() returns [CustomErrorType: message] when created without a message
  • log()返回[CustomErrorType: message],在创建时没有消息。
  • throw/stack provides the information at the point the error was created.
  • throw/stack提供了在创建错误时的信息。
  • Works optimally in Node.JS, and Chrome.
  • 在节点优化工作。JS和铬。
  • Will pass instanceof checks in Chrome, Safari, Firefox and IE 8+, but will not have a valid stack outside of Chrome/Safari. I'm OK with that because I can debug in chrome, but code which requires specific error types will still function cross browser. If you need Node only you can easily remove the if statements and you're good to go.
  • 将在Chrome、Safari、Firefox和IE 8+中通过instanceof检查,但不会在Chrome/Safari之外有一个有效的堆栈。我可以这样做,因为我可以在chrome中进行调试,但是需要特定错误类型的代码仍然可以使用跨浏览器。如果您需要节点,那么您可以轻松地删除If语句,并且您很好。

Snippet

片段

var CustomErrorType = function(message) {
    if (Object.defineProperty) {
        Object.defineProperty(this, "message", {
            value : message || "",
            enumerable : false
        });
    } else {
        this.message = message;
    }

    if (Error.captureStackTrace) {
        Error.captureStackTrace(this, CustomErrorType);
    }
}

CustomErrorType.prototype = new Error();
CustomErrorType.prototype.name = "CustomErrorType";

Usage

使用

var err = new CustomErrorType("foo");

Output

输出

var err = new CustomErrorType("foo");
console.log(err);
console.log(err.stack);

[CustomErrorType: foo]
CustomErrorType: foo
    at Object.<anonymous> (/errorTest.js:27:12)
    at Module._compile (module.js:456:26)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Function.Module.runMain (module.js:497:10)
    at startup (node.js:119:16)
    at node.js:906:3

/errorTest.js:30
        throw err;
              ^
CustomErrorType: foo
    at Object.<anonymous> (/errorTest.js:27:12)
    at Module._compile (module.js:456:26)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Function.Module.runMain (module.js:497:10)
    at startup (node.js:119:16)
    at node.js:906:3

#16


-1  

Try a new prototype object for each instance of the user defined error type. It allows instanceof checks to behave as usual plus type and message are correctly reported in Firefox and V8 (Chome, nodejs).

为用户定义的错误类型的每个实例尝试一个新的原型对象。它允许instanceof检查在Firefox和V8 (Chome, nodejs)中正确地报告了正常的+类型和消息。

function NotImplementedError(message){
    if(NotImplementedError.innercall===undefined){
        NotImplementedError.innercall = true;
        NotImplementedError.prototype = new Error(message);
        NotImplementedError.prototype.name = "NotImplementedError";
        NotImplementedError.prototype.constructor = NotImplementedError;

        return new NotImplementedError(message);
    }
    delete NotImplementedError.innercall;
}

Note that an additional entry will preceed the otherwise correct stack.

请注意,另一个条目将先于其他正确的堆栈。

#17


-3  

easier way. You could make your object inherit from the Error object. Example:

更简单的方法。您可以使对象从错误对象中继承。例子:

function NotImplementError(message)
{
    this.message = message;
    Error.call();
    Error.call(message);
} 

what we are doing is using the function call() which call the constructor of the Error class so is basicly the same thing as implementing a class inheritance in other object oriented languages.

我们所做的是使用函数调用(),它调用错误类的构造函数,所以基本等同于在其他面向对象语言中实现类继承。

#18


-3  

MDN has an excellent example:

MDN有一个很好的例子:

try {
  throw new Error('Whoops!');
} catch (e) {
  console.log(e.name + ': ' + e.message);
}

#1


172  

Update your code to assign your prototype to the Error.prototype and the instanceof and your asserts work.

更新代码以将原型分配给错误。原型和instanceof和你的断言工作。

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

However, I would just throw your own object and just check the name property.

但是,我只会抛出您自己的对象,并检查name属性。

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

Edit based on comments

编辑根据评论

After looking at the comments and trying to remember why I would assign prototype to Error.prototype instead of new Error() like Nicholas Zakas did in his article, I created a jsFiddle with the code below:

在看完评论并试着记住为什么我会把原型分配给错误。原型而不是新的错误()像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);
}

The console output was this.

控制台输出就是这个。

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

This confirmes the "problem" I ran into was the stack property of the error was the line number where new Error() was created, and not where the throw e occurred. However, that may be better that having the side effect of a NotImplementedError.prototype.name = "NotImplementedError" line affecting the Error object.

这证实了我遇到的“问题”是错误的堆栈属性是创建新错误()的行号,而不是抛出e发生的位置。但是,如果使用NotImplementedError.prototype.name = "NotImplementedError"行来影响错误对象,可能会更好。

Also, notice with NotImplementedError2, when I don't set the .name explicitly, it is equal to "Error". However, as mentioned in the comments, because that version sets prototype to new Error(), I could set NotImplementedError2.prototype.name = "NotImplementedError2" and be OK.

另外,请注意NotImplementedError2,当我没有显式地设置.name时,它等于“Error”。但是,正如在评论中提到的,因为那个版本将prototype设置为新的错误(),所以我可以设置NotImplementedError2.prototype.name = "NotImplementedError2",然后就可以了。

#2


81  

All of the above answers are terrible awful - really. Even the one with 107 ups! The real answer is here guys:

以上所有的答案都非常糟糕——真的。就是那个有107个包裹的人!真正的答案是:

Inheriting from the Error object - where is the message property?

从错误对象继承——消息属性在哪里?

TL;DR:

TL;博士:

A. The reason message isn't being set is that Error is a function that returns a new Error object and does not manipulate this in any way.

答:没有设置消息的原因是,错误是一个返回一个新的错误对象的函数,它不会以任何方式操作它。

B. The way to do this right is to return the result of the apply from the constructor, as well as setting the prototype in the usual complicated javascripty way:

这样做的方法是返回应用程序的结果,并在通常复杂的javascript方法中设置原型:

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();
*/

You could probably do some trickery to enumerate through all the non-enumerable properties of the tmp Error to set them rather than explicitly setting only stack and message, but the trickery isn't supported in ie<9

您可能会通过一些技巧来枚举tmp错误的所有不可枚举属性来设置它们,而不是显式地设置堆栈和消息,但是在ie<9中不支持欺骗。

#3


18  

If anyone is curious on how to create a custom error and get the stack trace:

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

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

#4


8  

In ES2015, you can use class to do this cleanly:

在2015年,您可以使用类来进行清洁:

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

This does not modify the global Error prototype, allows you to customize message, name, and other attributes, and properly captures the stack. It's also pretty readable.

这不会修改全局错误原型,允许您自定义消息、名称和其他属性,并正确地捕获堆栈。这也是非常可读的。

Of course, you may need to use a tool like babel if your code will be running on older browsers.

当然,如果您的代码在旧的浏览器上运行,您可能需要使用像babel这样的工具。

#5


7  

This section of the standard may explain why the Error.apply call doesn't initialize the object:

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

15.11.1 The Error Constructor Called as a Function

15.11.1错误构造函数称为函数。

When Error is called as a function rather than as a constructor, it creates and initialises a new Error object. Thus the function call Error(...) is equivalent to the object creation expression new Error(...) with the same arguments.

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

In this case the Error function probably determines that it's not being called as a constructor, so it returns a new Error instance rather than initializing the this object.

在这种情况下,错误函数可能决定了它不被称为构造函数,因此它返回一个新的错误实例,而不是初始化这个对象。

Testing with the following code seems to demonstrate that this is in fact what's happening:

用下面的代码进行测试似乎表明,这实际上是正在发生的事情:

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

The following output is generated when this is run:

在运行此操作时生成以下输出:

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

#6


6  

I had a similar issue to this. My error needs to be an instanceof both Error and NotImplemented, and it also needs to produce a coherent backtrace in the console.

我也有类似的问题。我的错误需要是一个错误和NotImplemented的实例,并且它还需要在控制台中产生一个一致的回溯。

My solution:

我的解决方案:

var NotImplemented = (function() {
  var NotImplemented, err;
  NotImplemented = (function() {
    function NotImplemented(message) {
      var err;
      err = new Error(message);
      err.name = "NotImplemented";
      this.message = err.message;
      if (err.stack) this.stack = err.stack;
    }
    return NotImplemented;
  })();
  err = new Error();
  err.name = "NotImplemented";
  NotImplemented.prototype = err;

  return NotImplemented;
}).call(this);

// TEST:
console.log("instanceof Error: " + (new NotImplemented() instanceof Error));
console.log("instanceof NotImplemented: " + (new NotImplemented() instanceofNotImplemented));
console.log("message: "+(new NotImplemented('I was too busy').message));
throw new NotImplemented("just didn't feel like it");

Result of running with node.js:

以node.js运行的结果:

instanceof Error: true
instanceof NotImplemented: true
message: I was too busy

/private/tmp/t.js:24
throw new NotImplemented("just didn't feel like it");
      ^
NotImplemented: just didn't feel like it
    at Error.NotImplemented (/Users/colin/projects/gems/jax/t.js:6:13)
    at Object.<anonymous> (/Users/colin/projects/gems/jax/t.js:24:7)
    at Module._compile (module.js:449:26)
    at Object.Module._extensions..js (module.js:467:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Module.runMain (module.js:487:10)
    at process.startup.processNextTick.process._tickCallback (node.js:244:9)

The error passes all 3 of my criteria, and although the stack property is nonstandard, it is supported in most newer browsers which is acceptable in my case.

这个错误传递了我的全部3个标准,尽管堆栈属性是非标准的,但在大多数更新的浏览器中都支持它,这在我的例子中是可以接受的。

#7


6  

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;

#8


3  

Accoring to Joyent you shouldn’t mess with the stack property (which I see in lots of answers given here), because it will have a negative impact on performance. Here is what they say:

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

stack: generally, don't mess with this. Don't even augment it. V8 only computes it if someone actually reads the property, which improves performance dramatically for handlable errors. If you read the property just to augment it, you'll end up paying the cost even if your caller doesn't need the stack.

stack:一般来说,不要搞砸了。甚至不增加。V8只有在有人真正读取该属性时才计算它,这将极大地提高可操作性错误的性能。如果您读取该属性只是为了增加它,那么即使您的调用者不需要堆栈,您也会付出代价。

I like and would like to mention their idea of wrapping the original error which is a nice replacement for passing on the stack.

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

So here is how I create a custom error, considering the above mentioned:

这里是我创建一个自定义错误的方法,考虑到上面提到的:

es5 version:

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 version:

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

I’ve put my solution into a module, here it is: https://www.npmjs.com/package/rerror

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

#9


2  

I just had to implement something like this and found that the stack was lost in my own error implementation. What I had to do was create a dummy error and retrieve the stack from that:

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

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

#10


2  

I used the Constructor Pattern to create the new error object. I defined the prototype chain such as an Error instance. See the MDN Error constructor reference.

我使用构造函数模式来创建新的错误对象。我定义了原型链,比如一个错误实例。请参阅MDN错误构造函数引用。

You can check this snippet on this gist.

您可以在这个要点上查看这个片段。

IMPLEMENTATION

// Creates user-defined exceptions
var CustomError = (function() {
  'use strict';

  //constructor
  function CustomError() {
    //enforces 'new' instance
    if (!(this instanceof CustomError)) {
      return new CustomError(arguments);
    }
    var error,
      //handles the arguments object when is passed by enforcing a 'new' instance
      args = Array.apply(null, typeof arguments[0] === 'object' ? arguments[0] : arguments),
      message = args.shift() || 'An exception has occurred';

    //builds the message with multiple arguments
    if (~message.indexOf('}')) {
      args.forEach(function(arg, i) {
        message = message.replace(RegExp('\\{' + i + '}', 'g'), arg);
      });
    }

    //gets the exception stack
    error = new Error(message);
    //access to CustomError.prototype.name
    error.name = this.name;

    //set the properties of the instance
    //in order to resemble an Error instance
    Object.defineProperties(this, {
      stack: {
        enumerable: false,
        get: function() { return error.stack; }
      },
      message: {
        enumerable: false,
        value: message
      }
    });
  }

  // Creates the prototype and prevents the direct reference to Error.prototype;
  // Not used new Error() here because an exception would be raised here,
  // but we need to raise the exception when CustomError instance is created.
  CustomError.prototype = Object.create(Error.prototype, {
    //fixes the link to the constructor (ES5)
    constructor: setDescriptor(CustomError),
    name: setDescriptor('JSU Error')
  });

  function setDescriptor(value) {
    return {
      configurable: false,
      enumerable: false,
      writable: false,
      value: value
    };
  }

  //returns the constructor
  return CustomError;
}());

USAGE

The CustomError constructor can receive many arguments to build the message, e.g.

CustomError构造函数可以接收许多参数来构建消息。

var err1 = new CustomError("The url of file is required"),
    err2 = new CustomError("Invalid Date: {0}", +"date"),
    err3 = new CustomError("The length must be greater than {0}", 4),
    err4 = new CustomError("Properties .{0} and .{1} don't exist", "p1", "p2");

throw err4;

And this is how the custom error looks:

这就是自定义错误的样子:

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

#11


1  

The constructor needs to be like a factory method and return what you want. If you need additional methods/properties, you can add them to the object before returning it.

构造函数需要像工厂方法一样,并返回您想要的。如果您需要其他方法/属性,您可以在返回之前将它们添加到对象中。

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

x = new NotImplementedError();

Though I'm not sure why you'd need to do this. Why not just use new Error... ? Custom exceptions don't really add much in JavaScript (or probably any untyped language).

虽然我不知道你为什么要这么做。为什么不使用新的错误…吗?自定义异常在JavaScript(或任何非类型化语言)中并没有真正的增加。

#12


1  

This is implemented nicely in the Cesium DeveloperError:

这在铯开发者的错误中很好地实现了:

In it's simplified form:

在简化的形式:

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

// Later on...

throw new NotImplementedError();

#13


0  

At the expense of not being able to use instanceof, the following preserves the original stack trace and doesn't use any non-standard tricks.

在不能够使用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
}

#14


0  

Another alternative , might not work in all enviroments.Atleast assured it works in nodejs 0.8 This approach uses a non standard way of modifying the internal proto prop

另一种选择,可能在所有环境中都不起作用。至少保证它在nodejs 0.8中工作,该方法使用一种非标准的方法来修改内部proto支持。

function myError(msg){ 
      var e = new Error(msg); 
      _this = this; 
      _this.__proto__.__proto__ = e;
}

#15


0  

If you are using Node/Chrome. The following snippet will get you extension which meets the following requirements.

如果您使用的是节点/Chrome。下面的代码片段将得到满足以下需求的扩展。

  • err instanceof Error
  • 犯错instanceof错误
  • err instanceof CustomErrorType
  • 犯错instanceof CustomErrorType
  • console.log() returns [CustomErrorType] when created with a message
  • 当创建消息时,console.log()返回[CustomErrorType]。
  • console.log() returns [CustomErrorType: message] when created without a message
  • log()返回[CustomErrorType: message],在创建时没有消息。
  • throw/stack provides the information at the point the error was created.
  • throw/stack提供了在创建错误时的信息。
  • Works optimally in Node.JS, and Chrome.
  • 在节点优化工作。JS和铬。
  • Will pass instanceof checks in Chrome, Safari, Firefox and IE 8+, but will not have a valid stack outside of Chrome/Safari. I'm OK with that because I can debug in chrome, but code which requires specific error types will still function cross browser. If you need Node only you can easily remove the if statements and you're good to go.
  • 将在Chrome、Safari、Firefox和IE 8+中通过instanceof检查,但不会在Chrome/Safari之外有一个有效的堆栈。我可以这样做,因为我可以在chrome中进行调试,但是需要特定错误类型的代码仍然可以使用跨浏览器。如果您需要节点,那么您可以轻松地删除If语句,并且您很好。

Snippet

片段

var CustomErrorType = function(message) {
    if (Object.defineProperty) {
        Object.defineProperty(this, "message", {
            value : message || "",
            enumerable : false
        });
    } else {
        this.message = message;
    }

    if (Error.captureStackTrace) {
        Error.captureStackTrace(this, CustomErrorType);
    }
}

CustomErrorType.prototype = new Error();
CustomErrorType.prototype.name = "CustomErrorType";

Usage

使用

var err = new CustomErrorType("foo");

Output

输出

var err = new CustomErrorType("foo");
console.log(err);
console.log(err.stack);

[CustomErrorType: foo]
CustomErrorType: foo
    at Object.<anonymous> (/errorTest.js:27:12)
    at Module._compile (module.js:456:26)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Function.Module.runMain (module.js:497:10)
    at startup (node.js:119:16)
    at node.js:906:3

/errorTest.js:30
        throw err;
              ^
CustomErrorType: foo
    at Object.<anonymous> (/errorTest.js:27:12)
    at Module._compile (module.js:456:26)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Function.Module.runMain (module.js:497:10)
    at startup (node.js:119:16)
    at node.js:906:3

#16


-1  

Try a new prototype object for each instance of the user defined error type. It allows instanceof checks to behave as usual plus type and message are correctly reported in Firefox and V8 (Chome, nodejs).

为用户定义的错误类型的每个实例尝试一个新的原型对象。它允许instanceof检查在Firefox和V8 (Chome, nodejs)中正确地报告了正常的+类型和消息。

function NotImplementedError(message){
    if(NotImplementedError.innercall===undefined){
        NotImplementedError.innercall = true;
        NotImplementedError.prototype = new Error(message);
        NotImplementedError.prototype.name = "NotImplementedError";
        NotImplementedError.prototype.constructor = NotImplementedError;

        return new NotImplementedError(message);
    }
    delete NotImplementedError.innercall;
}

Note that an additional entry will preceed the otherwise correct stack.

请注意,另一个条目将先于其他正确的堆栈。

#17


-3  

easier way. You could make your object inherit from the Error object. Example:

更简单的方法。您可以使对象从错误对象中继承。例子:

function NotImplementError(message)
{
    this.message = message;
    Error.call();
    Error.call(message);
} 

what we are doing is using the function call() which call the constructor of the Error class so is basicly the same thing as implementing a class inheritance in other object oriented languages.

我们所做的是使用函数调用(),它调用错误类的构造函数,所以基本等同于在其他面向对象语言中实现类继承。

#18


-3  

MDN has an excellent example:

MDN有一个很好的例子:

try {
  throw new Error('Whoops!');
} catch (e) {
  console.log(e.name + ': ' + e.message);
}