我有一组JavaScript“类”,其中基类定义了由继承类共享的函数。它正在工作,它的设置如下:
var ThingA = function(name) {
this.name = name;
};
ThingA.prototype = {
sayHi: function() {
alert('Hi, ' + this.name + '!');
}
};
var ThingB = function() {
ThingA.call(this, 'Charlie');
};
ThingB.prototype = new ThingA();
ThingB.prototype.constructor = ThingB;
var instanceOfB = new ThingB();
instanceOfB.sayHi(); // alerts 'Hi, Charlie!'
由于我无法控制的原因,我的公司在编写JavaScript时更喜欢遵循这种模式:
SomeClass = function() {
// "Private" functions go here
function somePrivateMethod() {
...
}
return {
// "Public" methods go here
somePublicMethod: function() { ... }
};
}();
现在,就事情而言,这很好,并且适用于许多情况。但它更像是一种功能性风格。只有一个“类”实例,一切都是静态的。
我被要求修改我的工作代码,以更贴近我公司喜欢的风格。所以我的问题是,有没有办法从包含在工厂类中的类继承?它看起来像这样:
FactoryClassA = function() {
var ThingA = function(name) {
this.name = name;
};
ThingA.prototype = {
sayHi: function() {
alert('Hi, ' + this.name + '!');
}
};
return {
createThingA: function(name) {
return new ThingA(name);
}
};
}();
FactoryClassB = function() {
// Define a ThingB class that inherits from ThingA somehow
return {
createThingB: function() {
return new ThingB();
}
};
}();
var instanceOfB = FactoryClassB.createThingB();
instanceOfB.sayHi(); // should alert 'Hi, Charlie!'
有没有办法定义ThingB
包含在FactoryClassB
中的ThingA
包含在FactoryClassA
中的<{1}}?感谢{{3}我知道我不能完全像这样做。我正在考虑使用一种方法来扩展给定的类......不知何故?
this question似乎很接近,但我无法弄清楚如何修改该示例的细节以适应我的具体情况。我愿意稍微改变一下我公司的惯常模式,但我能否至少接近它呢?
更新1
为了回应Adam的评论,只是在工厂类中添加一个参数,这就是我被困住的地方:
ThingB.prototype = new ThingA();
ThingB.prototype.constructor = ThingB;
如果我只是将参数传递给工厂类方法,我无法弄清楚如何调整这些行以使其工作。
答案 0 :(得分:4)
以下是(我相信)您正在寻找的内容:
FactoryClassA = function() {
var ThingA = function(name) {
this.name = name;
};
ThingA.prototype = {
sayHi: function() {
console.log('Hi, ' + this.name + '!');
}
};
// Add the constructor back to the prototype
// (see explanation below)
ThingA.prototype.constructor = ThingA;
return {
createThingA: function(name) {
return new ThingA(name);
}
};
}();
FactoryClassB = function() {
// Bootstrapping:
// Capture the instance, as we'll need it to set up the prototype
var baseInstance = new FactoryClassA.createThingA();
// Capture the constructor
var baseConstructor = baseInstance.constructor;
// Keep a reference to the base prototype
var baseProto = baseConstructor.prototype;
function ThingB(name) {
// Call base constructor, along with our args
baseConstructor.call(this, name);
};
ThingB.prototype = baseInstance;
ThingB.prototype.constructor = ThingB;
ThingB.prototype.sayHi = function() {
console.log('here I am');
// call the base class `sayHi`
baseProto.sayHi.call(this);
};
return {
createThingB: function(name) {
return new ThingB(name);
}
};
}();
// Testing
var foo = FactoryClassB.createThingB("Indeed");
foo.sayHi();
// Output:
// here I am
// hi indeed
<强>解释强>:
在FactoryClassA
中,这一行是必要的:
ThingA.prototype.constructor = ThingA;
请注意,JS中的每个原型都是通过对其构造函数的引用自动创建的。例如,当你这样做时:
function T(){}
T.prototype
已有一个名为constructor
的属性,该属性指向T
。
但是,在ThingA
的实施中,您可以通过ThingA.prototype = { ... }
重置整个原型。因此,您现在已经失去了对其构造函数的引用。在99%的情况下它是可以的,并且不会产生任何负面影响(这可能是大多数开发人员倾向于忘记它的原因)。但是,在继承的情况下,可能是必要的。
现在,在FactoryClassB
内,我们需要做一些自举:
var baseInstance = new FactoryClassA.createThingA();
var baseConstructor = baseInstance.constructor;
var baseProto = baseConstructor.prototype;
观察最后两行,因为它们对于在此设计模式中实现继承至关重要。首先,因为ThingA
的构造函数可以通过原型(ThingA.prototype.constructor = ThingA
)访问,所以它意味着给定ThingA
的实例,我们可以直接检索它的构造函数。由于构造函数本身就是函数,并且由于每个函数都有对其原型的引用,因此我们可以使用ThingA.prototype
保留baseConstructor.prototype
的引用。
接下来是关键部分,我们在其中设置了继承链:
function ThingB(name) {
// Call the base constructor
baseConstructor.call(this, name);
};
ThingB.prototype = baseInstance;
ThingB.prototype.constructor = ThingB;
上面的最后一行非常重要,因为它告诉原型它的构造函数是什么,否则它仍然会指向ThingA
。
你有它 - 原型继承。
旁注:
你可能会看到上面的内容如何变得相当繁琐,有点怪诞和重复。因此,您可能需要考虑像Fiber.js这样的继承库,它遵循您所需的封装模式(以及一些奖金,如mixins和decorators)。 免责声明:我创作了图书馆。