Ответ 1
Ниже приведено то, что (я считаю), которое вы ищете:
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
вы reset весь прототип, выполнив 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
.
Там у вас это - прототипное наследование.
Боковое примечание:
Вероятно, вы можете увидеть, как это может стать довольно утомительным, немного гротескным и повторяющимся. Ergo, вы можете рассмотреть библиотеку наследования типа Fiber.js, которая следует по шаблону инкапсуляции, который вам нужен (наряду с некоторыми бонусами, такими как миксины и декораторы), Отказ от ответственности: я создал библиотеку.