更新答案:
从您下面的评论中,回复我this.log()
应该有效的声明:
嗯,事情就是这样。当我在 Child 的测试函数中时,this
是一个空对象,所以我假设我没有得到正确的范围。
你还没有显示你是如何打电话test
的,但我怀疑这就是问题所在。如果您通过Child
实例调用它:
var c = new Child();
c.test();
...然后在调用中,this
将是子实例,它将(间接)继承Parent.prototype
对象及其log
属性。
但是你怎么称呼它很重要。这不起作用,例如:
var c = new Child();
var f = c.test;
f();
如果你这样做,在对函数的调用中,this
将是全局对象(或者undefined
如果你处于严格模式),而不是Child
实例。这是因为在 JavaScript 中,this
主要是通过调用函数的方式来设置的,而这样调用它并没有设置this
为您想要的。
这对于回调很重要,因为c.test
作为回调传入:
someFunctionThatUsesACallback(c.test);
...意味着不会this
为您设置回调代码。
如果您需要这样做,Function#bind
将有助于:
var f = c.test.bind(c); // Returns a version of c.test that, when called,
// will have `this` set to `c`
f(); // Works, `this` is the `Child` instance
同样:
someFunctionThatUsesACallback(c.test.bind(c));
更多(在我的博客上):
原答案:
如果您正确设置了原型层次结构,并且Child.prototype
没有log
它(并且您没有log
在实例上放置属性),那么您应该可以正常使用this.log();
。如果不能,则层次结构设置不正确。
我不知道是什么util.inherits
,但正确设置Child
和之间的关系Parent
并不复杂:
function Parent() {
}
Parent.prototype.log = function() {
console.log("log called");
};
function Child () {
Parent.call(this);
}
Child.prototype = Object.create(Parent.prototype);
Child.prototype.constructor = Child; // This line is largely optional, but a good idea
// Usage
var c = new Child();
c.log(); // "log called"
但是,如果您log
在您的实例中覆盖Child.prototype
或分配一个log
属性,并且您想使用Parent
的版本log
,那么您当然不能只使用this.log()
,因为该属性不再引用Parent.prototype.log
。
当你需要调用某些东西的父版本时(我称它们为“supercalls”,我认为这不是原始版本),你必须做更多的工作:
我通常通过将父构造函数传递给我用来构建孩子的函数来设置这样的层次结构,例如:
var Child = (function(Super) {
var pp = Super.prototype;
function Child() {
}
Child.prototype = Object.create(pp);
Child.prototype.doSomething = function() {
// Call `log` with appropriate `this`
pp.log.call(this);
};
return Child;
})(Parent);
通过始终使用该模式,我避免了Parent
在代码内部Child
编写(我使用Super
arg 代替),所以如果我需要 rebase Child
,我只需更改我传递给函数的内容。
因为那是相当丑陋的(例如,在顶部不清楚Child
它是从 派生的Parent
,因为Parent
在底部)并且涉及样板代码,我觉得不需要每次都重新编写,我为它写了一个简单的帮助脚本我打电话给Lineage
,这使它看起来像这样:
var Child = Lineage.define(Parent, function(p, pp) {
p.doSomething = function() {
// Call `log` with appropriate `this`
pp.log.call(this);
};
});
请注意,Lineage
将 theChild
和Parent
原型都作为参数传入,使其使用起来更加简洁(并且由于您可以选择这些参数名称,因此您可以使用任何适合您的术语 - 我使用p
正在创建的“类”的原型 [Child
在上面],以及pp
父母的原型等)。