创建自定义错误:了解MDN示例

时间:2018-07-08 18:30:02

标签: javascript error-handling

以下有关如何在JS中创建自定义错误的示例可以在MDN(link)上找到。

我正在努力了解正在发生的事情(下面的特定问题)。

function CustomError(foo, message, fileName, lineNumber) {
  var instance = new Error(message, fileName, lineNumber);
  instance.foo = foo;
  Object.setPrototypeOf(instance, Object.getPrototypeOf(this));
  if (Error.captureStackTrace) {
    Error.captureStackTrace(instance, CustomError);
  }
  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;
}


try {
  throw new CustomError('baz', 'bazMessage');
} catch (e) {
  console.log(e.foo); //baz
  console.log(e.message); //bazMessage
}

问题

  1. 由于我们要返回CustomError内部的对象,因此将其用作构造函数函数(new CustomError()),并将其用作普通函数对象({{1} })产生相同的结果?
  2. 在第11行:我们是否在这里创建了一个新对象,而不是直接将CustomError()设置为CustomError.prototype,以便我们可以扩展原型而不会影响所有其他Error.prototype对象? / li>
  3. 也在第11行:如果我们不能将其用作{em> constructor 函数(请参阅问题1),为什么还要设置函数的Error属性呢?
  4. 在第4行中,我们将prototype Error设置为所谓的函数,对吗?我不明白目的是什么/ instance的值是什么。
  5. this检查的目的是什么?

感谢您帮助我分析和理解此代码段。


编辑:

我想补充一点,我想我已经了解以下内容:

  • 每当我们使用 constructor 函数(captureStackTrace关键字)创建新对象时,它都会原型链接到一个空对象,而空对象又将原型链接到{{1} }
  • 该原型是链接到新的空对象的原型,而不是直接链接到new的原型,因为这样我们可以扩展新对象的原型,而无需更改Object.prototype上的所有对象的行为它的原型链。
  • 如果我们具有两个“继承”级别,并因此手动更改了构造函数函数的Object.prototype属性,则它应该反映相同的行为。实际上,我们应该将Object.prototype属性设置为一个空对象,而该对象又是链接到我们新引入的“父级”的原型

示例:

prototype

  • 如上所示,当手动更改prototype属性时,我们不仅应将新的空对象分配给function Person(name, gender) { this.name = name; this.gender = gender; } function Male(name) { Person.call(this, name, "male"); } Male.prototype = Object.create(Person.prototype, { constructor: { value: Male, enumerable: false, writeable: true } }); var person1 = new Male("Chris");属性,还应设置该空对象的prototype属性
  • 这是因为每个对象确实应该能够查看其原型以找出构造它的对象。这遵循prototype的行为,其中constructor属性为Object.prototype(与其他内置函数相同)

这应该解释第二个块。我正确理解了那部分吗?

2 个答案:

答案 0 :(得分:1)

  1. CustomError用作常规函数时,this的值位于其中undefined处。但是,如果将其用作构造函数,则this将引用当前实例。考虑到this被传递给getPrototypeOf,将其作为普通函数调用将引发错误并破坏代码。但是,如果CustomError的实施方式如下所示,将是相同的:

    function CustomError(){
        if(!(this instanceof MyError)) return new CustomError();
    }
    
  2. 那完全像你的想法

  3. 我不确定您为什么认为它不能用作构造函数,因为它已经在new语句中用try/catch关键字进行了调用

  4. 在第4行中,instance的原型设置为this的原型。 this指的是CustomError的当前实例,因为它是通过new调用的。

  5. 由于并非所有环境都支持Error.captureStackTrace(仅afaik chrome和nodejs支持它),因此它会在使用该功能之前检查该功能是否存在。


编辑零件

  • 每当我们使用构造函数(新关键字)创建新对象时,它都会原型链接到 Person.prototype (至少具有consctructor属性) ,这是链接到Object.prototype的原型

  • 它是链接到 Person.prototype 的原型,因为Person构造函数创建了它,构造函数将新创建的对象链接到他们自己的原型对象。是的,您可以更改Person.prototype而不会影响Object.prototype

答案 1 :(得分:1)

Error()是野兽。 调用其构造函数时,它仅忽略this并创建一个新的Error对象。 通常,在初始化自己的修改之前,可以使用Error.call(this,...)为其超类初始化新的子类实例。但是,使用Error,这将失败,因为您将返回初始化的新Error对象,并且实际实例保持不变。 Error()只是忽略其this并始终创建一个新实例。 为了避免这种情况,必须创建一个新的Error,然后将其原型更改为自定义原型(从Error.prototype派生),然后返回此实例,丢弃由最初的新对象创建的实例,例如Error这样做(因此我们必须在所有子类中复制Error的这种不良行为)。

以下作品:

MyErrorr = function() { // Class MyError
    var prototype = Object.create (Error.prototype, { name: {writable: false, enumerable: true, value: "MyError"}});
    function MyError (reason, callee) {
        if(reason instanceof MyError)
            return new MyError(reason.message); // clone
        // creating an Error is a bit tricky, as Error.call(this, message) behaves like new Error(message), creating a new instance
        var instance = new Error(reason);
        if(Error.captureStackTrace)
            Error.captureStackTrace(instance, callee?callee:MyError); // exclude the callee or at least MyError() from the stack trace
        var stack = instance.stack; // force creation of the stack output before the prototype is switched (else the formatting is different, no idea why)
        if(Object.setPrototypeOf)
            Object.setPrototypeOf(instance, prototype);
        else
            instance.__proto__ = prototype;
        return instance;
    };
    prototype.constructor = ConversionError;
    MyError.prototype = prototype;
    return MyError;
}();

由于未使用'this',因此可以使用和不使用new来实例化MyError(就像Error一样)

但是有一个问题: 所有这些存在的Error类型实际上是同一Error类的实例,而不是Error的子类。至少在Chrome上。另外,某些函数是Error的一部分,而不是Error.prototype的一部分,因此它们在您的类中不可用(例如captureStackTrace)。如果您将它们克隆,也许可以使用。