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

时间:2012-01-10 11:52:28

标签: javascript

我在Javascript中定义自定义错误对象时发现了一种奇怪的行为:

function MyError(msg) {
    Error.call(this, msg);
    this.name = "MyError";
}
MyError.prototype.__proto__ = Error.prototype;

var error = new Error("message");
error.message; // "message"

var myError = new MyError("message");
myError instanceof Error; // true
myError.message; // "" !

为什么new Error("message")设置了message属性,而Error.call(this, msg);没有?当然,我可以在this.message = msg构造函数中定义MyError,但我不太明白为什么它首先没有设置。

7 个答案:

答案 0 :(得分:37)

一个。就像,Raynos说的那样,message没有被设置的原因是Error是一个函数,它返回一个新的Error对象并且操纵this in无论如何。

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

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

    this.message = tmp.message
    // instead of this.stack = ..., a getter for more optimizy goodness
    Object.defineProperty(this, 'stack', {
        get: function () {
            return tmp.stack
        }
    })

    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()??)。

<强>更新

我创建了一个继承库来执行此操作^ https://github.com/fresheneesz/proto

答案 1 :(得分:16)

function MyError(msg) {
    var err = Error.call(this, msg);
    err.name = "MyError";
    return err;
}

Error不会操纵this,它会创建一个返回的新错误对象。这就是Error("foo")在没有new关键字的情况下工作的原因。

请注意,这是特定于实现的,v8(chrome&amp; node.js)的行为与此类似。

同样MyError.prototype.__proto__ = Error.prototype;是一种不好的做法。使用

MyError.prototype = Object.create(Error.prototype, { 
  constructor: { value: MyError } 
});

答案 2 :(得分:9)

在Node.js中,您可以创建如下自定义错误:

var util = require('util');

function MyError(message) {
  this.message = message;
  Error.captureStackTrace(this, MyError);
}

util.inherits(MyError, Error);

MyError.prototype.name = 'MyError';

请参阅节点文档中的captureStackTrace

答案 3 :(得分:1)

您可以使用Error.captureStackTrace过滤堆栈跟踪中不需要的行。

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

    this.message = tmp.message;
    /*this.stack = */Object.defineProperty(this, 'stack', { // getter for more optimizy goodness
        get: function() {
            return tmp.stack;
        }
    });

    Error.captureStackTrace(this, MyError); // showing stack trace up to instantiation of Error excluding it.

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

答案 4 :(得分:1)

在ES6中以这种方式进行操作有什么问题?

class MyError extends Error {
    constructor(message) {
        super(message);
        // Maintains proper stack trace (only on V8)
        if (Error.captureStackTrace) {
            Error.captureStackTrace(this, MyError);
        }
        this.appcode= 123; // can add custom props
    }
}

答案 5 :(得分:0)

另一种方法是使新的错误实例成为this的原型,这样你就不必知道要复制哪些属性,这解决了BT在最后谈到的问题。他们的答案。

function MyError() {
    if (this === undefined) {
        throw TypeError("MyError must be called as a constructor");
    }
    let newErr = Error.apply(undefined, arguments);
    Object.setPrototypeOf(newErr, MyError.prototype);
    Object.setPrototypeOf(this, newErr);
}
MyError.prototype = Object.create(Error.prototype);

let me = new MyError("A terrible thing happened");
console.log(me instanceof MyError);  // true
console.log(me instanceof Error);  // true
console.log(me.message);  // A terrible thing happened

为了我的钱,它有点整洁。 但是请注意,Object.setPrototypeOf() (或支持它的非ES6兼容实现上的object.__proto__ =可能会非常慢,因此如果您使用这些错误在你的黄金道路上,你可能不想这样做。

答案 6 :(得分:0)

我非常喜欢制作可重复使用的.js文件,这些文件几乎放在我参与的任何项目中。当我有时间它将成为一个模块。

对于我的错误,我创建了一个exceptions.js文件并将其添加到我的文件中。

以下是此文件中代码的示例:

const util = require('util');

/**
 * This exception should be used when some phat of code is not implemented.
 * @param {String} message Error message that will be used inside error.
 * @inheritDoc Error
 */
function NotImplementedException(message) {
  this.message = message;
  Error.captureStackTrace(this, NotImplementedException);
}

util.inherits(NotImplementedException, Error);

NotImplementedException.prototype.name = 'NotImplementedException';

module.exports = {
  NotImplementedException,
};

在我的项目的其他文件中,我必须在文件顶部有这个需要行。

const Exceptions = require('./exceptions.js');

要使用此错误,您只需要这个。

const err = Exceptions.NotImplementedException(`Request token ${requestToken}: The "${operation}" from "${partner}" does not exist.`);

完整方法实现的示例

const notImplemented = (requestToken, operation, partner) => {
  logger.warn(`Request token ${requestToken}: To "${operation}" received from "${partner}"`);
  return new Promise((resolve, reject) => {
    const err = Exceptions.NotImplementedException(`Request token ${requestToken}: The "${operation}" from "${partner}" does not exist.`);
    logger.error(err.message);
    return reject(err);
  });
};