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

cdl*_*ary 200 javascript exception

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

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

var nie = new NotImplementedError("some message");
console.log("The message is: '"+nie.message+"'")
Run Code Online (Sandbox Code Playgroud)

运行这个给出The message is: ''.有关为什么,或者是否有更好的方法来创建新Error子类的任何想法?是否存在我不知道apply的本机构Error造函数的问题?

Kev*_*son 186

更新您的代码以将您的原型分配给Error.prototype和instanceof以及您的断言工作.

function NotImplementedError(message) {
    this.name = "NotImplementedError";
    this.message = (message || "");
}
NotImplementedError.prototype = Error.prototype;
Run Code Online (Sandbox Code Playgroud)

但是,我只是抛出你自己的对象,只需检查name属性.

throw {name : "NotImplementedError", message : "too lazy to implement"}; 
Run Code Online (Sandbox Code Playgroud)

根据评论进行编辑

在查看评论并试图记住为什么我会分配原型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);
}
Run Code Online (Sandbox Code Playgroud)

控制台输出就是这个.

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
Run Code Online (Sandbox Code Playgroud)

这证实了我遇到的"问题"是错误的堆栈属性new Error()是创建的行号,而不是throw e发生的位置.但是,具有NotImplementedError.prototype.name = "NotImplementedError"影响Error对象的线的副作用可能更好.

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

  • 最好的答案,但直接采用`Error.prototype`可能是不好的形式.如果你以后想要添加一个`NotImplementedError.prototype.toString`,那么对象现在别名为`Error.prototype.toString` - 更好的做法是'NotImplementedError.prototype = new Error()`. (45认同)
  • 根据https://code.google.com/p/chromium/issues/detail?id=228909`subslass.prototype = new Error()`是错误的表单.你应该使用`subclass.prototype = Object.create(superclass.prototype)`来代替.我希望它也可以修复堆栈跟踪问题. (27认同)
  • 获得有意义的堆栈跟踪的简单技巧是在构造函数中生成错误并保存它的堆栈.它会为构造函数提供正确的调用堆栈+ 1行(这是一个合适的回报):`this.stack = new Error().stack;` (8认同)
  • -1; 这是错的.做`NotImplementedError.prototype = Error.prototype;'不会让`instanceof`将`NotImplementedError`视为`Error`的*子类*,它使`instanceof`将它们视为完全相同的类.如果您将上面的代码粘贴到您的控制台并尝试`new Error()instanceof NotImplementedError`,您将得到`true`,这显然是错误的. (5认同)
  • 在所有原型设备中我仍然有点迷失.为什么在您的示例中为this.name指定名称而不是NotImplementedError.prototype.name?你能回答吗,这对我的理解至关重要:) (4认同)
  • 我知道我将原型设置为新的Error()有问题,但不能确定.我认为这给出了错误的堆栈或亚麻的错误信息. (3认同)
  • 我无法回答Kevin,但我的猜测是因为他将NotImplementedError的原型设置为Error.prototype,因此如果他更改了NotImplementedError.prototype的名称,他真的会更改Error.prototype的名称.但是,如果他将原型设置为新的Error(),就像cdleary所建议的那样,这不是问题,并且实际上最好在原型上设置名称. (2认同)
  • 此外,对于函数声明,您不需要`this.name`,仅用于未命名的函数表达式.在您的示例中,javascript引擎将为您设置`name`. (2认同)

B T*_*B T 87

所有上述答案都非常糟糕 - 真的.即便是107人的!真正的答案是这里的人:

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

TL; DR:

答:原因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();
*/
Run Code Online (Sandbox Code Playgroud)

你也许可以做一些弄虚作假通过的所有非枚举的属性来枚举tmp错误设置它们,而不是只明确设置stackmessage,但挂羊头卖狗肉并不在IE <9支撑

  • 我简化并改进了这种方法:http://jsbin.com/rolojuhuya/1/edit?js,console (13认同)
  • @MattKantor也许能得到答案?我想我最喜欢你的. (3认同)
  • 此解决方案还可用于实例化现有错误的自定义错误.如果您正在使用第三方库并希望使用您自己的自定义类型包装现有错误,则其他方法无法正常工作.仅供参考,您可以通过传递现有错误来实例化vanilla Errors. (2认同)
  • 而不是`temp.name = this.name ='MyError'`,你可以做`temp.name = this.name = this.constructor.name`.这样它也适用于`MyError`的子类. (2认同)

rat*_*ray 26

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

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

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

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

  • 请注意,如果您使用“babel”,它会受到无法扩展内置“Error”类的限制:https://babeljs.io/docs/en/caveats/#classes (4认同)
  • 有关扩展错误的更多详细信息,请在此处查看 **自定义错误** https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error (4认同)

ris*_*sto 21

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

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);
}
Run Code Online (Sandbox Code Playgroud)


gra*_*cha 10

class NotImplementedError extends Error {
  constructor(message) {
    super(message);
    this.message = message;
  }
}
NotImplementedError.prototype.name = 'NotImplementedError';
module.exports = NotImplementedError;
Run Code Online (Sandbox Code Playgroud)

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);
}
Run Code Online (Sandbox Code Playgroud)

它只是这个答案的类表示。

输出

NotImplementedError: NotImplementedError message
  ...stacktrace
ex1 instanceof NotImplementedError = true
ex1 instanceof Error = true
ex1.name = NotImplementedError
ex1.message = NotImplementedError message
Run Code Online (Sandbox Code Playgroud)


Dav*_*ave 7

标准的这一部分可以解释为什么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");
Run Code Online (Sandbox Code Playgroud)

运行此命令时会生成以下输出:

returned.message = 'some message'
this.message = ''
Run Code Online (Sandbox Code Playgroud)


Pie*_*sin 7

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;
Run Code Online (Sandbox Code Playgroud)

  • 这是最好的答案.它是succint,以这种方式创建的异常将在所有情况下都能正常运行.它还保留了堆栈跟踪,这在非平凡的应用程序中非常重要.我只会用"prototype = Object.create(Error.prototype)"替换"prototype = new Error()".对于Node.js,有一个小型库可以为您执行此操作:https://www.npmjs.com/package/node-custom-errors (3认同)

sin*_*unk 6

我有一个类似的问题.我的错误需要有一个instanceofErrorNotImplemented,而且还需要产生在控制台连贯回溯.

我的解决方案

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");
Run Code Online (Sandbox Code Playgroud)

使用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)
Run Code Online (Sandbox Code Playgroud)

该错误通过了我的所有3个标准,虽然该stack属性是非标准的,但在大多数较新的浏览器中都支持它,在我的情况下是可接受的.


Dom*_*nic 6

我喜欢这样做:

  • 使用name所以 toString() 抛出"{code}: {message}"
  • 将相同的东西返回给 super 以便它在堆栈跟踪中显示相同
  • 附加代码error.code作为检查/解析代码在代码中比检查消息更好,例如您可能想要本地化
  • 将消息附加到error.message作为替代error.toString()

class AppException extends Error {
  constructor(code, message) {
    const fullMsg = message ? `${code}: ${message}` : code;
    super(fullMsg);
    this.name = code;
    this.code = code;
    this.message = fullMsg;
  }
  
  toString() {
    return this.message;
  }
}

// Just a code
try {
  throw new AppException('FORBIDDEN');
} catch(e) {
  console.error(e);
  console.error(e.toString());
  console.log(e.code === 'FORBIDDEN');
}

// A code 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());
  console.log(e.code === 'FORBIDDEN');
}
Run Code Online (Sandbox Code Playgroud)


bor*_*kur 5

根据Joyent的观点,您不应该对stack属性感到困惑(我在此处给出的很多答案中都可以看到),因为它会对性能产生负面影响。他们说的是:

堆栈:通常,请勿对此感到困惑。甚至不要增加它。V8仅在有人实际读取属性时才进行计算,从而极大地提高了可处理错误的性能。如果您只是为了增加属性而阅读该属性,那么即使您的呼叫者不需要该堆栈,您也将最终要支付费用。

我喜欢并且想提到他们包装原始错误的想法,这是传递堆栈的好方法。

因此,考虑到上述情况,这是我如何创建自定义错误:

es5版本:

function RError(options) {
    options = options || {}; // eslint-disable-line no-param-reassign
    this.name = options.name;
    this.message = options.message;
    this.cause = options.cause;

    // capture stack (this property is supposed to be treated as private)
    this._err = new Error();

    // create an iterable chain
    this.chain = this.cause ? [this].concat(this.cause.chain) : [this];
}
RError.prototype = Object.create(Error.prototype, {
    constructor: {
        value: RError,
        writable: true,
        configurable: true
    }
});

Object.defineProperty(RError.prototype, 'stack', {
    get: function stack() {
        return this.name + ': ' + this.message + '\n' + this._err.stack.split('\n').slice(2).join('\n');
    }
});

Object.defineProperty(RError.prototype, 'why', {
    get: function why() {
        var _why = this.name + ': ' + this.message;
        for (var i = 1; i < this.chain.length; i++) {
            var e = this.chain[i];
            _why += ' <- ' + e.name + ': ' + e.message;
        }
        return _why;
    }
});

// usage

function fail() {
    throw new RError({
        name: 'BAR',
        message: 'I messed up.'
    });
}

function failFurther() {
    try {
        fail();
    } catch (err) {
        throw new RError({
            name: 'FOO',
            message: 'Something went wrong.',
            cause: err
        });
    }
}

try {
    failFurther();
} catch (err) {
    console.error(err.why);
    console.error(err.stack);
    console.error(err.cause.stack);
}
Run Code Online (Sandbox Code Playgroud)

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);
}
Run Code Online (Sandbox Code Playgroud)

我已将解决方案放入模块中,在这里是:https : //www.npmjs.com/package/rerror