【发布时间】:2013-09-22 11:47:50
【问题描述】:
[关于这个主题还有其他类似的问题,但没有一个回答我在这里提出的问题,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 的类似问题需要a lengthy treatise,我无法合理地期待我的问题在这里得到完整的答案,但我希望我能得到一个指向这样完整答案的指针。
更新:我尝试了 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: 开头的行。这意味着MyErr 中forEach 循环中的console.log 语句永远不会被执行(大概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 确实出现在 Firebug 的变量检查器中 tmp 的属性中,以及不计其数的 em> 其他也没有显示在上面的控制台输出中)。
此外,显式设置的name 属性确实出现在myerr.toString() 的输出中,但此方法的行为方式仍与error.toString() 不同。
如果我在 MyErr 函数的末尾添加以下行,我确实会从 myerr.toString() 获得预期的输出:
this.message = message;
...但是我并没有从中得到什么安慰,因为这个动作,连同我上面展示的大多数其他动作,都是针对我发布的特定示例的特设技巧,但这些只是为了作为一个似乎更深层次问题的说明,即预期继承模型的完全分解。
这个问题的目的是找出如何从Error.prototype实现“真正的继承”,如果这不可能,那么尽可能地模拟它,有一个清晰的图片 模拟未能实现完整继承模型的程度。我强调,这两个替代方案中的后者不应与每次我们碰巧注意到一些不符合预期的新方式时增量修补一些有缺陷的实现的策略相混淆。这种“增量修补”方法为无声的 bug 提供了理想的环境,因此是疯狂的秘诀。
UPDATE2: JS 的不可预测性似乎没有尽头。我刚刚发现,至少在 Firebug 控制台中,Object.getOwnPropertyNames 会根据其参数是非原子表达式还是先前分配了相同非原子表达式的变量而产生不同的值。 wtf?
例如,以下内容是直接从 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 对象这一事实有关,即使它不是thrown。即使删除所有内容也会出现相同的输出在该行中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 不适合扶手椅程序员! :)
【问题讨论】:
-
@FelixKling:谢谢。我已经阅读了这些以及其他类似的 SO 线程。他们都解释了为什么一个特定的 构造不能工作。这并没有解决我的问题。我的问题是“如何实现一个完全继承自
Error.prototype的对象”。请参阅我链接的Array上的文章,了解我尝试使用Error实现的目标。 -
错误具有本机代码 ctor 和方法。您只能 a) 扩展错误对象,或编写执行您想要的操作并在内部使用错误对象的包装器。
-
firebug 和 chrome 控制台是神奇的环境,它们没有与普通 js 环境完全相同的属性。在 chrome 控制台中特别困扰我的一件事是,如果您在控制台中将对象设置为 null,则对象将永远保留。
标签: javascript oop inheritance prototypal-inheritance