代码之家  ›  专栏  ›  技术社区  ›  cdleary

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

  •  185
  • cdleary  · 技术社区  · 15 年前

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

    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 子类?有什么问题吗 apply 对本地人 误差 我不知道的建造师?

    20 回复  |  直到 6 年前
        1
  •  183
  •   JLRishe    8 年前

    更新代码,将原型分配给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() 就像尼古拉斯·扎卡斯 article 我创造了一个 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
    

    这证实了我遇到的“问题”是错误的堆栈属性是行号,其中 新错误() 是被创建的,而不是 throw e 发生。然而,这可能比具有 NotImplementedError.prototype.name = "NotImplementedError" 影响错误对象的行。

    另外,请注意 NotImplementedError2 ,当我不设置 .name 明确地说,它等于“错误”。但是,正如注释中提到的,因为该版本将原型设置为 新错误() 我可以设定 NotImplementedError2.prototype.name = "NotImplementedError2" 好吧。

        2
  •  85
  •   Community Lee    7 年前

    以上所有的答案都很糟糕——真的。即使是那个有107个UPS的!真正的答案在这里,伙计们:

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

    DR:

    A. The理性 message 不是设定的是 Error 是一个函数,它返回一个新的错误对象并执行 操纵 this 无论如何。

    B.这样做的方法是从构造函数返回应用的结果,并以通常复杂的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();
    */

    您可能会做一些小技巧来枚举 tmp 设置它们而不是仅显式设置时出错 stack 消息 ,但IE<9不支持这种诡计

        3
  •  20
  •   ThomasReggi    9 年前

    如果有人对如何创建自定义错误感兴趣 获取堆栈跟踪:

    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
  •  14
  •   rattray    7 年前

    在ES2015中,您可以使用 class 要彻底做到这一点:

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

    这不会修改全局 Error 原型,允许您自定义 message , name 以及其他属性,并正确捕获堆栈。它的可读性也很好。

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

        5
  •  7
  •   Dave    11 年前

    本标准的这一节可以解释 Error.apply 调用不初始化对象:

    15.11.1作为函数调用的错误构造函数

    当错误被作为函数而不是构造函数调用时,它创建并 初始化新的错误对象。因此,函数调用错误(…)是 相当于对象创建表达式new 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 = ''
    
        6
  •  6
  •   sinisterchipmunk    11 年前

    我也有类似的问题。我的错误应该是 instanceof 二者都 Error NotImplemented ,它还需要在控制台中生成一个一致的回溯。

    我的解决方案:

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

    使用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)
    

    这个错误通过了我的三个标准,尽管 stack 属性是非标准的, it is supported in most newer browsers 这在我的情况下是可以接受的。

        7
  •  6
  •   Pierre Voisin    11 年前
    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
  •  4
  •   borisdiakur Faheel Khan    6 年前

    Accoring to Joyent 您不应该弄乱stack属性(我在这里给出的很多答案中都看到了),因为它会对性能产生负面影响。他们是这样说的:

    堆栈: 一般来说,不要搞砸了。甚至不要增加它。V8仅在有人实际读取属性时才计算它,这显著提高了可处理错误的性能。如果您只是为了增加属性,那么即使您的调用者不需要堆栈,您也会为此付出代价。

    我喜欢并且想提及 their idea of wrapping the original error 这是一个很好的替代品。

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

    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

        9
  •  2
  •   Jules    12 年前

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

    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
  •   jherax    7 年前

    我使用构造函数模式来创建新的错误对象。我定义了 原型链 比如一个 Error 实例。参见 Error constructor 参考文献。

    你可以在上面检查这个片段 gist .

    实施

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

    用法

    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;
    

    这就是自定义错误的外观:

    Custom error prototype chain

        11
  •  1
  •   pluckyglen    15 年前

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

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

    不过我不知道你为什么要这么做。为什么不只用 new Error...

        12
  •  1
  •   Aram Kocharyan    11 年前

    var NotImplementedError = function(message) {
        this.name = 'NotImplementedError';
        this.message = message;
        this.stack = (new Error()).stack;
    }
    
    // Later on...
    
    throw new NotImplementedError();
    
        13
  •  1
  •   Rafal Enden    6 年前

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

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

    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());
      }
    });
    
        14
  •  0
  •   Gima    11 年前

    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
    }
    
        15
  •  0
  •   kleopatra Aji kattacherry    11 年前

    function myError(msg){ 
          var e = new Error(msg); 
          _this = this; 
          _this.__proto__.__proto__ = e;
    }
    
        16
  •  0
  •   Owen Allen    10 年前

    • err instanceof Error
    • err instanceof CustomErrorType
    • [CustomErrorType]
    • [CustomErrorType: message]
    • if

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

    var err = new CustomErrorType("foo");
    

    产量

    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
    
        17
  •  0
  •   nanobar    6 年前

    我喜欢这样做,所以消息在stacktrace或toString中是相同的,我可以只传递一个名称或名称和消息。在引发HTTP错误时很有用,例如,处理程序可以 error.toString() 对于用户,它将优雅地处理您的错误或任何其他错误。

    class AppException extends Error {
      constructor(code, message) {
        const fullMsg = message ? `${code}: ${message}` : code;
        super(fullMsg);
        this.name = code;
        this.message = fullMsg;
      }
      
      toString() {
        return this.message;
      }
    }
    
    // Just an error name
    try {
      throw new AppException('Forbidden');
    } catch(e) {
      console.error(e);
      console.error(e.toString());
    }
    
    // A name and a message
    try {
      throw new AppException('Forbidden', 'You don\'t have access to this page');
    } catch(e) {
      console.error(e);
      console.error(e.toString());
    }
        18
  •  -1
  •   Augustus Kling    12 年前

    为用户定义的错误类型的每个实例尝试一个新的原型对象。它允许 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;
    }
    

    请注意,一个额外的条目将在原本正确的堆栈之前进行处理。

        19
  •  -3
  •   Paulo Enmanuel    11 年前

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

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

    我们要做的是使用函数call(),它调用错误类的构造函数,因此基本上与在其他面向对象的语言中实现类继承相同。

        20
  •  -3
  •   Ronnie Smith    7 年前

    有一个 excellent example :

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