[关于这个主题还有其他类似的问题,但没有一个回答我在这里提出的问题,AFAICT。(即我读过的答案都解释了为什么一个特定的结构不能与提问者试图做的事情有关,并且在某些情况下,它们提供了获得预期结果的替代方法。但是没有线程回答如何实现真正的继承的问题来自Error.prototype
.)]
我设法做的最好的事情仍然基本上没用:
function MyErr(message) {
var tmp = Error.apply(this, arguments);
for (var prop in tmp) { this[prop] = tmp[prop]; }
}
MyErr.prototype = Object.create(Error.prototype);
即使在“手动”复制属性之后(“继承失败”的明确迹象),返回的对象new MyErr("whatever")
仍然远不接近我认为的“继承自Error.prototype
”的实例。与预期行为的偏差实在太多了,无法在此处列出——它们在我看到的任何地方都会出现!——但是,对于初学者来说,
console.log((new Error("some error")).toString()) // "Error: some error"
console.log((new MyErr("another error")).toString()) // "Error"
// expected
// "MyError: another error"
console.log((new Error("some error")).constructor) // "Error()"
console.log((new MyErr("another error")).constructor) // "Error()"
// expected:
// "MyError()"
(如果有人想知道,不,不是在'循环constructor
中复制的属性之一。我检查了。)MyErr
for
一个对象可以真正继承自Error.prototype
吗?
鉴于要回答类似的问题Array
需要冗长的论文,我不能合理地期待我的问题在这里得到完整的答案,但我希望我能得到一个指向这样完整答案的指针。
更新:我尝试了 Bergi 的提议。下面我提供了一个完整的、独立的实现。1
<!DOCTYPE html>
<html><head><meta charset="utf-8"></head><body>
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<script>
jQuery(document).ready(function ($) {
function MyErr(message) {
var tmp = Error.apply(this, arguments);
Object.getOwnPropertyNames(tmp).forEach(function(p) {
console.log('property: ' + p);
Object.defineProperty(this, p,
Object.getOwnPropertyDescriptor(tmp, p));
}, this);
console.log('done creating ' + this);
}
MyErr.prototype = Object.create(Error.prototype, {
constructor: {value:MyErr, configurable:true},
name: {value:"MyErr", configurable:true}
});
var error = new Error("some error");
var myerr = new MyErr("another error");
console.log(error.toString());
console.log(myerr.toString());
console.log(error.constructor);
console.log(myerr.constructor);
});
</script></body></html>
我在 Firebug 控制台中得到的输出是这样的:
done creating MyErr testjs.html (line 13)
Error: some error testjs.html (line 23)
MyErr testjs.html (line 24)
Error() testjs.html (line 26)
MyErr(message) testjs.html (line 27)
请特别注意,输出不包括任何以 开头的行property:
。这意味着循环中的console.log
语句永远不会被执行(并且对于回调的其余部分可能也是如此)。forEach
MyErr
forEach
如果我forEach
用
for (var p in tmp) {
console.log('property: ' + p);
Object.defineProperty(this, p,
Object.getOwnPropertyDescriptor(tmp, p));
};
...然后在输出前添加三个新行(省略行号):
property: fileName
property: lineNumber
property: columnNumber
这表明,甚至不是以这种方式可访问message
的tmp
属性之一(尽管message
确实出现tmp
在 Firebug 的变量检查器中的 's 属性中,以及上面控制台输出中也没有显示的大量其他属性中)。
此外,显式设置的name
属性确实出现在 的输出中myerr.toString()
,但该方法的行为仍与error.toString()
.
myerr.toString()
如果我在函数末尾添加以下行,我确实会得到预期的输出MyErr
:
this.message = message;
...但是我对此并没有什么安慰,因为这个动作,连同我上面展示的大多数其他动作,都是针对我发布的具体示例的特设技巧,但这些只是为了说明似乎是一个更深层次的问题,即预期继承模型的完全分解。
这个问题的目的是找出如何从 实现“真正的继承” Error.prototype
,如果这不可能,那么尽可能地模拟它,清楚地了解模拟未能实现完整的程度继承模型。我强调,这两个替代方案中的后一个不应该与每次我们碰巧注意到一些不符合预期的新方式时逐步修补一些有缺陷的实现的策略相混淆。这种“增量修补”方法为无声错误提供了理想的环境,因此是疯狂的秘诀。
UPDATE2: JS 的不可预测性似乎没有尽头。我刚刚发现,至少在 Firebug 控制台中,Object.getOwnPropertyNames
根据其参数是非原子表达式还是先前分配了相同非原子表达式的变量,会产生不同的值。 什么?
例如,以下内容是直接从 Firebug 控制台中的交互复制粘贴的(为了便于阅读,我在每个命令前添加了一个空行):
>>> Object.getOwnPropertyNames(new Error("not assigned"))
[]
>>> errorvar = new Error("assigned")
Error: assigned
(no source for debugger eval code)
>>> Object.getOwnPropertyNames(errorvar)
["fileName", "lineNumber", "message", "stack"]
(赋值后的输出errorvar
似乎与Error
创建对象有关,即使它不是throw
n。即使删除new
该行左侧的所有内容,也会出现相同的输出。)
如果这还不够,如果我从脚本中运行它
console.log(Object.getOwnPropertyNames(new Error("not assigned")))
var errorvar = new Error("assigned");
console.log(Object.getOwnPropertyNames(errorvar));
Firebug 控制台中的输出是
[ ]
[ ]
我不知道这种不稳定的行为是由于 ECMAScript5、JavaScript、Firefox、Firebug 还是什么,但它让我发疯了......
1我发布了这个独立的实现来鼓励其他人尝试一下。如果我从 JavaScript 中学到的一件事是,无论代码多么简单,无论您认为自己对 JavaScript 了解多少,了解 JavaScript 代码将要做什么的唯一方法就是运行它。可悲的是,JavaScript 编程仍然是一个令人尴尬的实验活动。JS 不适合扶手椅程序员!:)