从本地对象继承

6

在Javascript中,我似乎对构造函数链继承以及原生对象有些遗漏。例如:

function ErrorChild(message) { Error.call(this, message); }
ErrorChild.prototype = Object.create(Error.prototype);
var myerror = new ErrorChild("Help!");

为什么在执行这些语句之后,myerror.message 被定义为 ""?我本来期望 Error 构造函数会将其定义为 "Help!"(并覆盖默认值Error.prototype.message),就像我执行下面的代码一样:
var myerror = new Error("Help!")

非常感谢!
2个回答

4

简单的解决方法:工作示例

function ErrorChild(name, message) {
    // Error.call(this); this is not needed
    this.name = name;
    this.message = message;
}

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

var myerror = new ErrorChild("test", "Help!");
document.body.innerHTML += myerror.message;

上述操作不会破坏预期行为。当您抛出myerror时,正确的namemessage将显示。
问题:
根据ECMA5语言规范:
15.11.1函数调用Error构造函数
当Error被作为函数而非构造函数调用时,它会创建并初始化一个新的Error对象。因此函数调用Error(...)等同于具有相同参数的new Error(...)对象创建表达式。
问题在于:Error.call(this)等同于new Error。但是new Error实例化不会设置namemessage。默认情况下,new Error将使用""初始化message
15.11.4.3 Error.prototype.message # Ⓣ Ⓡ Error.prototype.message的初始值为空字符串。

测试(证明)

如果在您的ErrorChild中添加:

var test = Error.call(this, message);
console.dir(test);
console.log(test instanceof Error); // true;
console.log(test.message); // "Help!";

test 反映了 ECMA5 规范。具有适当的 message 设置的 Error 实例。

结论:

因为 Error.call(arguments); 自动转换为 new Error(arguments);,所以作用域丢失,因此属性从未在 this 对象上初始化。

当使用 Object.create(Error.prototype) 时,message 属性采用默认值,即空字符串。


嗯,我等着看你是否会更新你的答案来解释问题,我以为你不会;现在我也回答了 :D - Moritz Roessler
谢谢,这回答了我的问题!还有其他的本地对象会显示类似这样的奇怪行为吗?例如,我尝试了相同的代码,但将Error替换为String,它似乎也存在问题(我的StringChild实例无法像普通字符串一样使用)。 - ehmicky
1
为了正确性,请在您的构造函数中删除 Error.call(this),因为它会创建一个不必要的对象,在构造函数完成时被垃圾回收。 =) - Moritz Roessler
1
@alex23,即使只有ErrorChild.prototype = Error.prototype也足够了=) - Moritz Roessler
你似乎没有理解 Object.create。请修复你的代码。 - Bergi
显示剩余3条评论

1
问题不在于继承链,而是Error作为函数调用时的行为类似于调用构造函数,实例化一个new Error对象。
因此,当调用new ErrorChild时,实际上执行的代码相当于:
function ErrorChild(message) {
 new Error (message) 
}

等一下。但是这只创建了一个新的错误对象,它没有在任何地方使用(也没有被您的构造函数返回)。并且很容易被遗忘。
因此,这实际上不会修改您的实际ErrorChild实例。
因此,您访问的message属性是由ErrorChild.prototype屏蔽的那个,它继承自Error.protyotype,因此包含message默认值为空字符串"" 但是您可以简单地“模拟”一个错误对象。
因为您只需要toString方法定义的行为和2个属性
  • name
  • message
来覆盖错误的标准属性。
您只需将构造函数的原型设置为Error.prototype,然后手动分配namemessage即可。
function ErrorChild (name,message) {
  this.name = name;
  this.message = message;
}
ErrorChild.prototype = Error.prototype;

现在,您只需创建一个new ErrorChild (...)实例即可。您可以在logcatchthrow它 -> throw new ErrorChild("CustomError","Help") //Uncaught CustomError: Help 这里有一个JSBin演示

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接