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!'
有没有办法定义包含在FactoryClassB中的ThingB,它继承自FactoryClassA中包含的ThingA?感谢this question,我知道我不会这样做.我正在考虑使用一种方法来扩展给定的类…不知何故?
This answer似乎很接近,但是我无法弄清楚如何修改这个例子的细节,以适应我的情况.我愿意弯曲我公司的一般模式,但是我至少可以靠近它吗?
更新1
为了回应Adam的意见,只需要向工厂类添加一个参数,这里就是我被困住的地方:
ThingB.prototype = new ThingA(); ThingB.prototype.constructor = ThingB;
我不知道如何适应这些行,使其工作,如果我只是传入一个参数到工厂类方法.
解决方法
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已经有一个称为构造函数的属性,它返回到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的一个实例,我们可以直接检索其构造函数.由于构造函数是函数本身,并且由于每个函数都有对其原型的引用,所以我们可以使用baseConstructor.prototype来保存ThingA.prototype的引用.
接下来是关键部分,我们设置了继承链:
function ThingB(name) {
// Call the base constructor
baseConstructor.call(this,name);
};
ThingB.prototype = baseInstance;
ThingB.prototype.constructor = ThingB;
上面的最后一行是非常重要的,因为它告诉原型它的构造函数是什么,否则它仍然指向ThingA.
你有它 – 原型继承.
边注:
你可能会看到上述如何可以相当乏味,有点奇怪,重复. Ergo,你可能想考虑像Fiber.js这样的继承库,它遵循你想要的封装模式(以及一些奖金,如mixins和decorator).免责声明:我创作了图书馆.