什么是在JavaScript中扩展错误的好方法?

时间:2009-09-05 00:54:58

标签: javascript exception error-handling

我想在我的JS代码中抛出一些东西,我希望它们是instanceof Error,但我也想让它们成为别的东西。

在Python中,通常会有一个子类Exception。

在JS中做什么是适当的?

25 个答案:

答案 0 :(得分:188)

唯一的标准字段Error对象是message属性。 (请参阅MDN或EcmaScript语言规范,第15.11节) 其他所有内容都是特定于平台的。

大多数环境设置stack属性,但fileNamelineNumber在继承中几乎没用。

所以,简约方法是:

function MyError(message) {
    this.name = 'MyError';
    this.message = message;
    this.stack = (new Error()).stack;
}
MyError.prototype = new Error;  // <-- remove this if you do not 
                                //     want MyError to be instanceof Error

您可以嗅探堆栈,从中移除不需要的元素并提取fileName和lineNumber等信息,但这样做需要有关当前正在运行的JavaScript平台的信息。大多数情况是不必要的 - 如果你真的想要,你可以在验尸中做到。

Safari 是一个值得注意的例外。没有stack属性,但throw关键字设置了正在抛出的对象的sourceURLline属性。这些事情保证是正确的。

我使用的测试用例可以在这里找到:JavaScript self-made Error object comparison

答案 1 :(得分:130)

在ES6中:

fileStream.pipe(parser).pipe(stringifier).pipe(writeStream)

source

答案 2 :(得分:42)

修改:请阅读评论。事实证明这只适用于V8(Chrome / Node.JS)我的目的是提供一个跨浏览器的解决方案,该解决方案适用于所有浏览器,并提供支持的堆栈跟踪。

编辑:我制作了这个社区Wiki,以便进行更多编辑。

V8(Chrome / Node.JS)解决方案适用于Firefox,可以修改为在IE中正常运行。 (见帖子末尾)

function UserError(message) {
  this.constructor.prototype.__proto__ = Error.prototype // Make this an instanceof Error.
  Error.call(this) // Does not seem necessary. Perhaps remove this line?
  Error.captureStackTrace(this, this.constructor) // Creates the this.stack getter
  this.name = this.constructor.name; // Used to cause messages like "UserError: message" instead of the default "Error: message"
  this.message = message; // Used to set the message
}

Original post on "Show me the code !"

简短版本:

function UserError(message) {
  this.constructor.prototype.__proto__ = Error.prototype
  Error.captureStackTrace(this, this.constructor)
  this.name = this.constructor.name
  this.message = message
}

我在函数中保留this.constructor.prototype.__proto__ = Error.prototype以保持所有代码在一起。但您也可以将this.constructor替换为UserError,这样您就可以将代码移到函数外部,因此只能调用一次。

如果你走这条路,请确保在第一次投掷UserError之前调用行。

该警告不适用于该函数,因为无论顺序如何,都会首先创建函数。因此,您可以将函数移动到文件的末尾,而不会出现问题。

浏览器兼容性

适用于Firefox和Chrome(以及Node.JS)并填写所有承诺。

Internet Explorer在以下

中失败
  • 错误没有err.stack开头,所以“这不是我的错”。

  • Error.captureStackTrace(this, this.constructor)不存在,因此您需要执行其他操作,例如

    if(Error.captureStackTrace) // AKA if not IE
        Error.captureStackTrace(this, this.constructor)
    
  • 当您继承toString时,
  • Error不再存在。所以你还需要添加。

    else
        this.toString = function () { return this.name + ': ' + this.message }
    
  • IE不会将UserError视为instanceof Error,除非您在throw UserError

    之前的某个时间运行以下内容
    UserError.prototype = Error.prototype
    

答案 3 :(得分:35)

简而言之:

  • 如果您使用ES6 没有转发

    class CustomError extends Error { /* ... */}
    
  • 如果您使用 Babel transpiler

选项1:使用babel-plugin-transform-builtin-extend

选项2:自己动手(灵感来自同一个图书馆)

    function CustomError(...args) {
      const instance = Reflect.construct(Error, args);
      Reflect.setPrototypeOf(instance, Reflect.getPrototypeOf(this));
      return instance;
    }
    CustomError.prototype = Object.create(Error.prototype, {
      constructor: {
        value: Error,
        enumerable: false,
        writable: true,
        configurable: true
      }
    });
    Reflect.setPrototypeOf(CustomError, Error);
  • 如果您使用纯ES5

    function CustomError(message, fileName, lineNumber) {
      var instance = new Error(message, fileName, lineNumber);
      Object.setPrototypeOf(instance, Object.getPrototypeOf(this));
      return instance;
    }
    CustomError.prototype = Object.create(Error.prototype, {
      constructor: {
        value: Error,
        enumerable: false,
        writable: true,
        configurable: true
      }
    });
    if (Object.setPrototypeOf){
        Object.setPrototypeOf(CustomError, Error);
    } else {
        CustomError.__proto__ = Error;
    }
    
  • 替代方案:使用Classtrophobic框架

<强>解释

为什么使用ES6和Babel扩展Error类是个问题?

因为不再能识别出CustomError的实例。

class CustomError extends Error {}
console.log(new CustomError('test') instanceof Error);// true
console.log(new CustomError('test') instanceof CustomError);// false

事实上,根据Babel的官方文档,您cannot extend any built-in JavaScript classes,例如DateArrayDOMError

这里描述了这个问题:

其他SO答案怎么样?

所有给出的答案都解决了instanceof问题,但您丢失了常规错误console.log

console.log(new CustomError('test'));
// output:
// CustomError {name: "MyError", message: "test", stack: "Error↵    at CustomError (<anonymous>:4:19)↵    at <anonymous>:1:5"}

使用上述方法,您不仅可以解决instanceof问题,还可以保留常规错误console.log

console.log(new CustomError('test'));
// output:
// Error: test
//     at CustomError (<anonymous>:2:32)
//     at <anonymous>:1:5

答案 4 :(得分:26)

对于每种不同类型的错误,避免样板,我将一些解决方案的智慧结合到createErrorType函数中:

function createErrorType(name, init) {
  function E(message) {
    if (!Error.captureStackTrace)
      this.stack = (new Error()).stack;
    else
      Error.captureStackTrace(this, this.constructor);
    this.message = message;
    init && init.apply(this, arguments);
  }
  E.prototype = new Error();
  E.prototype.name = name;
  E.prototype.constructor = E;
  return E;
}

然后您可以轻松定义新的错误类型,如下所示:

var NameError = createErrorType('NameError', function (name, invalidChar) {
  this.message = 'The name ' + name + ' may not contain ' + invalidChar;
});

var UnboundError = createErrorType('UnboundError', function (variableName) {
  this.message = 'Variable ' + variableName + ' is not bound';
});

答案 5 :(得分:25)

2018 中,我认为这是最好的方式;支持IE9 +和现代浏览器。

更新:有关不同实施的比较,请参阅this testrepo

function CustomError(message) {
    Object.defineProperty(this, 'name', {
        enumerable: false,
        writable: false,
        value: 'CustomError'
    });

    Object.defineProperty(this, 'message', {
        enumerable: false,
        writable: true,
        value: message
    });

    if (Error.hasOwnProperty('captureStackTrace')) { // V8
        Error.captureStackTrace(this, CustomError);
    } else {
        Object.defineProperty(this, 'stack', {
            enumerable: false,
            writable: false,
            value: (new Error(message)).stack
        });
    }
}

if (typeof Object.setPrototypeOf === 'function') {
    Object.setPrototypeOf(CustomError.prototype, Error.prototype);
} else {
    CustomError.prototype = Object.create(Error.prototype, {
        constructor: { value: CustomError }
    });
}

还要注意__proto__属性为deprecated,这在其他答案中广泛使用。

答案 6 :(得分:19)

为了完整起见 - 只是因为之前的答案都没有提到这个方法 - 如果你正在使用Node.js并且不必关心浏览器兼容性,那么所需的效果很容易实现if [ "$c" ]; then ...模块(official docs here)的内置inherits

例如,假设您要创建一个自定义错误类,该错误类将错误代码作为第一个参数,将错误消息作为第二个参数:

文件 custom-error.js

util

现在您可以实例化并传递/抛出您的'use strict'; var util = require('util'); function CustomError(code, message) { Error.captureStackTrace(this, CustomError); this.name = CustomError.name; this.code = code; this.message = message; } util.inherits(CustomError, Error); module.exports = CustomError;

CustomError

请注意,使用此代码段,堆栈跟踪将具有正确的文件名和行,并且错误实例将具有正确的名称!

这是因为var CustomError = require('./path/to/custom-error'); // pass as the first argument to your callback callback(new CustomError(404, 'Not found!')); // or, if you are working with try/catch, throw it throw new CustomError(500, 'Server Error!'); 方法的使用,它在目标对象上创建了一个captureStackTrace属性(在这种情况下,正在实例化stack)。有关其工作原理的详细信息,请查看文档here

答案 7 :(得分:18)

答案 8 :(得分:12)

这个解决方案怎么样?

而不是使用以下方式抛出自定义错误:

throw new MyError("Oops!");

你会包装Error对象(有点像装饰器):

throw new MyError(Error("Oops!"));

这可确保所有属性都正确,例如stack,fileName lineNumber等等。

然后你要做的就是复制属性,或者为它们定义getter。 以下是使用getter(IE9)的示例:

function MyError(wrapped)
{
        this.wrapped = wrapped;
        this.wrapped.name = 'MyError';
}

function wrap(attr)
{
        Object.defineProperty(MyError.prototype, attr, {
                get: function()
                {
                        return this.wrapped[attr];
                }
        });
}

MyError.prototype = Object.create(Error.prototype);
MyError.prototype.constructor = MyError;

wrap('name');
wrap('message');
wrap('stack');
wrap('fileName');
wrap('lineNumber');
wrap('columnNumber');

MyError.prototype.toString = function()
{
        return this.wrapped.toString();
};

答案 9 :(得分:9)

我的解决方案比提供的其他答案更简单,并没有缺点。

它保留Error原型链和Error上的所有属性,而无需具体了解它们。它已在Chrome,Firefox,Node和IE11中进行了测试。

唯一的限制是调用堆栈顶部的额外条目。但这很容易被忽视。

以下是一个包​​含两个自定义参数的示例:

function CustomError(message, param1, param2) {
    var err = new Error(message);
    Object.setPrototypeOf(err, CustomError.prototype);

    err.param1 = param1;
    err.param2 = param2;

    return err;
}

CustomError.prototype = Object.create(
    Error.prototype,
    {name: {value: 'CustomError', enumerable: false}}
);

使用示例:

try {
    throw new CustomError('Something Unexpected Happened!', 1234, 'neat');
} catch (ex) {
    console.log(ex.name); //CustomError
    console.log(ex.message); //Something Unexpected Happened!
    console.log(ex.param1); //1234
    console.log(ex.param2); //neat
    console.log(ex.stack); //stacktrace
    console.log(ex instanceof Error); //true
    console.log(ex instanceof CustomError); //true
}

对于需要setPrototypeOf的polyfil的环境:

Object.setPrototypeOf = Object.setPrototypeOf || function (obj, proto) {
    obj.__proto__ = proto;
    return obj;
};

答案 10 :(得分:8)

在上面的例子中,Error.apply(也Error.call)对我没有任何作用(Firefox 3.6 / Chrome 5)。我使用的解决方法是:

function MyError(message, fileName, lineNumber) {
    var err = new Error();

    if (err.stack) {
        // remove one stack level:
        if (typeof(Components) != 'undefined') {
            // Mozilla:
            this.stack = err.stack.substring(err.stack.indexOf('\n')+1);
        }
        else if (typeof(chrome) != 'undefined' || typeof(process) != 'undefined') {
            // Google Chrome/Node.js:
            this.stack = err.stack.replace(/\n[^\n]*/,'');
        }
        else {
            this.stack = err.stack;
        }
    }
    this.message    = message    === undefined ? err.message    : message;
    this.fileName   = fileName   === undefined ? err.fileName   : fileName;
    this.lineNumber = lineNumber === undefined ? err.lineNumber : lineNumber;
}

MyError.prototype = new Error();
MyError.prototype.constructor = MyError;
MyError.prototype.name = 'MyError';

答案 11 :(得分:7)

正如有些人所说,使用ES6相当容易:

class CustomError extends Error { }

所以我在我的应用程序中尝试了这个(Angular,Typescript),它只是没有用。过了一段时间后,我发现问题来自于Typescript:O

请参阅https://github.com/Microsoft/TypeScript/issues/13965

这非常令人不安,因为如果你这样做:

class CustomError extends Error {}
​

try {
  throw new CustomError()
} catch(e) {
  if (e instanceof CustomError) {
    console.log('Custom error');
  } else {
    console.log('Basic error');
  }
}

在节点或直接进入浏览器时,它会显示:Custom error

尝试在Typescript playground上的项目中使用Typescript运行它,它会显示Basic error ...

解决方案是执行以下操作:

class CustomError extends Error {
  // we have to do the following because of: https://github.com/Microsoft/TypeScript/issues/13965
  // otherwise we cannot use instanceof later to catch a given type
  public __proto__: Error;

  constructor(message?: string) {
    const trueProto = new.target.prototype;
    super(message);

    this.__proto__ = trueProto;
  }
}

答案 12 :(得分:3)

我的2美分:

为什么要另一个答案?

a)因为访问Error.stack属性(如在某些答案中)具有很大的性能损失。

b)因为它只有一行。

c)因为https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error处的解决方案似乎没有保留堆栈信息。

//MyError class constructor
function MyError(msg){
    this.__proto__.__proto__ = Error.apply(null, arguments);
};

用法示例

http://jsfiddle.net/luciotato/xXyeB/

它做了什么?

this.__proto__.__proto__MyError.prototype.__proto__,因此设置__proto__ FOR ALL INSTANCES MyError对特定的新创建的错误。它保留MyError类属性和方法,并将新的Error属性(包括.stack)放在__proto__链中。

明显的问题:

使用有用的堆栈信息,您不能拥有多个MyError实例。

如果您不完全了解this.__proto__.__proto__=的作用,请不要使用此解决方案。

答案 13 :(得分:3)

我只想补充其他人已经说过的内容:

要确保自定义错误类在堆栈跟踪中正确显示,您需要将自定义错误类的prototype的name属性设置为自定义错误类的name属性。 这就是我的意思:

CustomError.prototype = Error.prototype;
CustomError.prototype.name = 'CustomError';

所以完整的例子是:

    var CustomError = function(message) {
        var err = new Error(message);
        err.name = 'CustomError';
        this.name = err.name;
        this.message = err.message;
        //check if there is a stack property supported in browser
        if (err.stack) {
            this.stack = err.stack;
        }
        //we should define how our toString function works as this will be used internally
        //by the browser's stack trace generation function
        this.toString = function() {
           return this.name + ': ' + this.message;
        };
    };
    CustomError.prototype = new Error();
    CustomError.prototype.name = 'CustomError';

完成所有操作后,您抛出新的异常并且它看起来像这样(我在Chrome开发工具中懒得尝试过这个):

CustomError: Stuff Happened. GASP!
    at Error.CustomError (<anonymous>:3:19)
    at <anonymous>:2:7
    at Object.InjectedScript._evaluateOn (<anonymous>:603:39)
    at Object.InjectedScript._evaluateAndWrap (<anonymous>:562:52)
    at Object.InjectedScript.evaluate (<anonymous>:481:21)

答案 14 :(得分:2)

由于JavaScript异常难以分类,因此我不会成为子类。我只是创建一个新的Exception类并在其中使用Error。我更改了Error.name属性,使其在控制台上看起来像我的自定义异常:

var InvalidInputError = function(message) {
    var error = new Error(message);
    error.name = 'InvalidInputError';
    return error;
};

上述新异常可以像常规错误一样抛出,并且可以按预期工作,例如:

throw new InvalidInputError("Input must be a string");
// Output: Uncaught InvalidInputError: Input must be a string 

警告:堆栈跟踪并不完美,因为它会将您带到创建新错误的位置而不是您抛出的位置。这对Chrome来说不是什么大问题,因为它直接在控制台中为您提供完整的堆栈跟踪。但是,例如,Firefox上的问题更多。

答案 15 :(得分:2)

正如Mohsen的回答所指出的,在ES6中,可以使用类扩展错误。它更容易,并且它们的行为与本机错误更加一致......但不幸的是,如果您需要支持ES6之前的浏览器,在浏览器中使用它并不是一件简单的事情。请参阅下面有关如何实施的一些说明,但与此同时,我建议采用一种相对简单的方法,其中包含其他答案中的一些最佳建议:

function CustomError(message) {
    //This is for future compatibility with the ES6 version, which
    //would display a similar message if invoked without the
    //`new` operator.
    if (!(this instanceof CustomError)) {
        throw new TypeError("Constructor 'CustomError' cannot be invoked without 'new'");
    }
    this.message = message;

    //Stack trace in V8
    if (Error.captureStackTrace) {
       Error.captureStackTrace(this, CustomError);
    }
    else this.stack = (new Error).stack;
}
CustomError.prototype = Object.create(Error.prototype);
CustomError.prototype.name = 'CustomError';

在ES6中,它很简单:

class CustomError extends Error {}

...并且您可以使用try {eval('class X{}')检测对ES6类的支持,但如果您尝试将ES6版本包含在由旧版加载的脚本中,则会出现语法错误浏览器。因此,支持所有浏览器的唯一方法是为支持ES6的浏览器动态加载单独的脚本(例如通过AJAX或eval())。更复杂的是,eval()并非在所有环境中都受支持(由于内容安全策略),这可能是也可能不是您项目的考虑因素。

所以现在,无论是上面的第一种方法,还是直接简单地使用Error而不试图扩展它似乎是对于需要支持非ES6浏览器的代码实际上可以做到的最佳方法。

有些人可能会考虑另外一种方法,即使用Object.setPrototypeOf()创建一个错误对象,该错误对象是您的自定义错误类型的一个实例,但其外观和行为更多就像控制台中的本机错误一样(感谢Ben's answer推荐)。以下是我对这种方法的看法:https://gist.github.com/mbrowne/fe45db61cea7858d11be933a998926a8。但考虑到有一天我们能够使用ES6,我个人不确定这种方法的复杂性是否值得。

答案 16 :(得分:1)

我不喜欢所有其他答案,太长、太复杂或没有正确跟踪堆栈。这是我的方法,如果您需要更多自定义道具,请将它们传递给构造函数并将它们设置为名称。

class CustomError extends Error {
  constructor (message) {
    super(message)

    // needed for CustomError instanceof Error => true
    Object.setPrototypeOf(this, new.target.prototype);

    // Set the name
    this.name = this.constructor.name

    // Maintains proper stack trace for where our error was thrown (only available on V8)
    if (Error.captureStackTrace) {
      Error.captureStackTrace(this, this.constructor)
    }
  }
}

// create own CustomError sub classes
class SubCustomError extends CustomError{}

// Tests
console.log(new SubCustomError instanceof CustomError) // true
console.log(new SubCustomError instanceof CustomError) // true 
console.log(new CustomError instanceof Error) // true
console.log(new SubCustomError instanceof Error) // true

throw new SubCustomError ('test error')

答案 17 :(得分:1)

Mohsen在ES6上面有一个很好的答案,它设置了名称,但是如果您使用的是TypeScript,或者您生活在未来,希望此proposal for public and private class fields超过了第3阶段的提案并并将其作为ECMAScript / JavaScript的一部分进入第4阶段,那么您可能想知道它要短一些。第3阶段是浏览器开始实现功能的地方,因此,如果您的浏览器支持,则下面的代码可能会起作用。 (在新的Edge浏览器v81中进行了测试,它似乎工作正常)。请注意,尽管此功能目前尚不稳定,应谨慎使用,并且应始终检查浏览器对不稳定功能的支持。这篇文章主要针对浏览器可能支持的未来居民。要检查支持,请检查MDNCan I use。目前,它在浏览器市场上获得了66%的支持,但是这种支持还不算太好,因此,如果您真的想立即使用它,并且不想等待,请使用Babel这样的编译器或{{3 }}。

class EOFError extends Error { 
  name="EOFError"
}
throw new EOFError("Oops errored");

将此错误与无名错误进行比较,抛出该错误将不会记录其名称。

class NamelessEOFError extends Error {}
throw new NamelessEOFError("Oops errored");

答案 18 :(得分:1)

我建议的解决方案是使用 error 的 .name 属性来区分错误类型而不是 instancof

这并不能完全回答问题,但我认为对于某些情况,这是一个合理的解决方案。

我所看到的拥有 instanceof CustomError 的好处是您可以在承诺捕获处理程序中进行自定义处理。

例如:

class CustomError extends Error {/** ... **/}

axios
  .post(url, payload)
  .then(data => {
    if (!data.loggedIn) throw CustomError("not logged in");
    return data;
  })
  .catch(error => {
    if (error instanceof CustomError) {/** custom handling of error*//}
    throw error
  })

如果这就是您要实现的目标,那么 .name 参数也很适合您:

export const ERROR_NOT_LOGGED_IN = "ERROR_NOT_LOGGED_IN";

axios
  .post(url, payload)
  .then(data => {
    if (!data.loggedIn) throw Error("not logged in").name=ERROR_NOT_LOGGED_IN ;
    return data;
  })
  .catch(error => {
    if (error.name === ERROR_NOT_LOGGED_IN) {/** custom handling of error*//}
    throw error
  })

答案 19 :(得分:1)

这样做的方法是从构造函数返回apply的结果,以及以通常复杂的javascripty方式设置原型:

function MyError() {
    var tmp = Error.apply(this, arguments);
    tmp.name = this.name = 'MyError'

    this.stack = tmp.stack
    this.message = tmp.message

    return this
}
    var IntermediateInheritor = function() {}
        IntermediateInheritor.prototype = Error.prototype;
    MyError.prototype = new IntermediateInheritor()

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 ...>

此时这种方式的唯一问题(我已经迭代了一点)是

  • stackmessage以外的其他属性未包含在MyError
  • stacktrace有一个额外的行,这不是必需的。

第一个问题可以通过使用此答案中的技巧迭代所有不可枚举的错误属性来修复:Is it possible to get the non-enumerable inherited property names of an object?,但是不支持ie&lt; 9。第二个问题可以通过撕掉堆栈跟踪中的那条线来解决,但我不确定如何安全地执行此操作(可能只是删除了第二行的e.stack.toString()??)。

答案 20 :(得分:0)

该代码段显示了所有内容。

function add(x, y) {
      if (x && y) {
        return x + y;
      } else {
        /**
         * 
         * the error thrown will be instanceof Error class and InvalidArgsError also
         */
        throw new InvalidArgsError();
        // throw new Invalid_Args_Error(); 
      }
    }

    // Declare custom error using using Class
    class Invalid_Args_Error extends Error {
      constructor() {
        super("Invalid arguments");
        Error.captureStackTrace(this);
      }
    }

    // Declare custom error using Function
    function InvalidArgsError(message) {
      this.message = `Invalid arguments`;
      Error.captureStackTrace(this);
    }
    // does the same magic as extends keyword
    Object.setPrototypeOf(InvalidArgsError.prototype, Error.prototype);

    try{
      add(2)
    }catch(e){
      // true
      if(e instanceof Error){
        console.log(e)
      }
      // true
      if(e instanceof InvalidArgsError){
        console.log(e)
      }
    }

答案 21 :(得分:0)

在Node上,就像其他人所说的那样,很简单:

class DumbError extends Error {
    constructor(foo = 'bar', ...params) {
        super(...params);

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

        this.name = 'DumbError';

        this.foo = foo;
        this.date = new Date();
    }
}

try {
    let x = 3;
    if (x < 10) {
        throw new DumbError();
    }
} catch (error) {
    console.log(error);
}

答案 22 :(得分:0)

如果您不关心错误的表现,这是您可以做的最小的事情

Object.setPrototypeOf(MyError.prototype, Error.prototype)
function MyError(message) {
    const error = new Error(message)
    Object.setPrototypeOf(error, MyError.prototype);
    return error
}

您可以在没有新的MyError(message)的情况下使用它

通过在调用构造函数Error之后更改原型,我们不必设置调用堆栈和消息

答案 23 :(得分:0)

自定义错误装饰器

这基于George Bailey's answer,但扩展并简化了原始想法。它是用CoffeeScript编写的,但很容易转换为JavaScript。这个想法是通过包装它的装饰器扩展Bailey的自定义错误,允许您轻松创建新的自定义错误。

注意:这仅适用于V8。在其他环境中不支持Error.captureStackTrace

定义

装饰器为错误类型命名,并返回一个接收错误消息的函数,并包含错误名称。

CoreError = (@message) ->

    @constructor.prototype.__proto__ = Error.prototype
    Error.captureStackTrace @, @constructor
    @name = @constructor.name

BaseError = (type) ->

    (message) -> new CoreError "#{ type }Error: #{ message }"

使用

现在创建新的错误类型很简单。

StorageError   = BaseError "Storage"
SignatureError = BaseError "Signature"

为了好玩,您现在可以定义一个函数,如果调用了过多的args,则抛出SignatureError

f = -> throw SignatureError "too many args" if arguments.length

这已经过相当好的测试,似乎在V8上完美运行,维护了追溯,位置等。

注意:构建自定义错误时,使用new是可选的。

答案 24 :(得分:0)

我会后退一步,考虑一下为什么要这样做?我认为重点是以不同的方式处理不同的错误。

例如,在Python中,您可以将catch语句限制为仅捕获MyValidationError,并且您可能希望能够在javascript中执行类似的操作。

catch (MyValidationError e) {
    ....
}

您无法在javascript中执行此操作。只会有一个陷阱。您应该对错误使用if语句来确定其类型。

catch(e) { if(isMyValidationError(e)) { ... } else { // maybe rethrow? throw e; } }

我想我会抛出一个带有类型,消息和你认为合适的任何其他属性的原始对象。

throw { type: "validation", message: "Invalid timestamp" }

当你发现错误时:

catch(e) {
    if(e.type === "validation") {
         // handle error
    }
    // re-throw, or whatever else
}