Ответ 1
Проблема (s)
- Конструктор класса
Base
вызывается один раз и только один раз.
this.prototype = new baseClass; - Привилегированные методы будут ссылаться на один и тот же объект
this
через экземпляры, потому что эти методы создаются внутри конструктора (который вызывался только один раз).
Проблема (ы), основанная на мнении
- Вы должны попытаться избежать модификации собственных прототипов (т.е.
Function.prototype
), если вы планируете работать вместе с JavaScript, который у вас нет.
Решение
- Вызов конструктора и родительского конструктора для каждого созданного нового экземпляра.
- Наследовать цепочку прототипов родителя (не экземпляр родительского класса).
- Поддерживает соотношение количества экземпляров, созданных к числу вызовов конструктора (-ов) в цепочке прототипов, в соотношении 1:1.
Окончательное решение проблемы Max
Обратите особое внимание на функцию inherits
и строку ParentClass.call(this, title);
.
Конструкторы для поиска - ParentClass
и ChildClass
/**
* Allows a child constructor to safely inherit the parent constructors prototype chain.
* @type {Function}
* @param {!Function} childConstructor
* @param {!Function} parentConstructor
*/
function inherits(childConstructor, parentConstructor){
var TempConstructor = function(){};
TempConstructor.prototype = parentConstructor.prototype; // Inherit parent prototype chain
childConstructor.prototype = new TempConstructor(); // Create buffer object to prevent assignments directly to parent prototype reference.
childConstructor.prototype.constructor = childConstructor; // Reset the constructor property back the the child constructor
};
//////////////////////////////////////
/** @constructor */
function ParentClass(title) {
this.setTitle(title);
var randId_ = Math.random();
/** @return {number} */
this.getPrivlegedRandId = function()
{return randId_;};
};
/** @return {string} */
ParentClass.prototype.getTitle = function()
{return this.title_;};
/** @param {string} value */
ParentClass.prototype.setTitle = function(value)
{this.title_ = value;};
//////////////////////////////////////
/**
* @constructor
* @param {string} title
* @param {string} name
*/
ChildClass = function (name, title) {
ParentClass.call(this, title); // Call the parent class constructor with the required arguments
this.setName(name);
}
inherits(ChildClass, ParentClass); // Inherit the parent class prototype chain.
/** @return {string} */
ChildClass.prototype.getName = function()
{return this.name_;};
/** @param {string} value */
ChildClass.prototype.setName = function(value)
{this.name_ = value;};
Вниз кроличьи отверстия
Для тех, кому интересно, почему это работает, просто запомнив его функцией inherits
.
Как разрешаются свойства с использованием цепи прототипа
Если свойство не найдено на уровне экземпляра, JavaScript попытается устранить недостающее свойство, выполнив поиск по цепочке прототипов конструкторов экземпляров. Если свойство не найдено в первом объекте прототипа, он будет искать объект-прототип родителя и т.д. Вплоть до Object.prototype
. Если он не может найти его в Object.prototype
, тогда будет выброшена ошибка.
Вызов родительского конструктора из дочернего конструктора: Попытка # 1
// Bad
var ChildConstructor = function(arg1, arg2, arg3){
var that = new ParentConstructor(this, arg1, arg2, arg3);
that.getArg1 = function(){return arg1};
return that;
}
Любая переменная, созданная с помощью new ChildConstructor
, вернет экземпляр ParentConstructor
.
ChildConstructor.prototype
не будет использоваться.
Вызов родительского конструктора из дочернего конструктора: Попытка # 2
// Good
var ChildConstructor = function(arg1, arg2, arg3){
ParentConstructor.call(this, arg1, arg2, arg3);
}
Теперь конструктор и родительский конструктор вызываются соответствующим образом. Однако существуют только методы, определенные в конструкторе (конструкциях). Свойства исходных прототипов не будут использоваться, поскольку они еще не связаны с прототипом дочерних конструкторов.
Наследование родительского прототипа: Попытка # 1
// Bad
ChildConstructor.prototype = new ParentConstructor();
Родительский конструктор будет либо вызываться только один раз, либо один раз в зависимости от того, используется ли ParentConstructor.call(this)
.
Унаследовать попытку прототипа родителя # 2
// Bad
ChildConstructor.prototype = ParentConstructor.prototype;
Хотя это технически работает, любые присваивания ChildConstructor.prototype
также будут назначены ParentConstructor.prototype
, потому что объекты передаются по ссылке, а не копией.
Унаследовать попытку прототипа родителя # 3
// Almost there
var TempConstructor = function(){};
TempConstructor.prototype = ParentConstructor.prototype;
ChildConstructor.prototype = new TempConstructor();
Это позволяет присваивать свойства ChildConstructor.prototype
, поскольку это экземпляр временной анонимной функции.
Свойства, которые не найдены в экземпляре TempConstructor
, будут проверять цепочку прототипов для этого свойства, поэтому вы успешно унаследовали родительский прототип. Единственная проблема заключается в том, что ChildConstructor.prototype.constructor
теперь указывает на TempConstructor
.
Унаследовать попытку прототипа родителя # 4
// Good
var TempConstructor = function(){};
TempConstructor.prototype = ParentConstructor.prototype;
ChildConstructor.prototype = new TempConstructor();
ChildConstructor.prototype.constructor = ChildConstructor;
Все вместе
var ParentConstructor = function(){
};
var ChildConstructor = function(){
ParentConstructor.call(this)
};
var TempConstructor = function(){};
TempConstructor.prototype = ParentConstructor.prototype;
ChildConstructor.prototype = new TempConstructor();
ChildConstructor.prototype.constructor = ChildConstructor;
Вы успешно унаследовали родительский класс! Посмотрим, сможем ли мы сделать лучше.
Наследует функцию
function inherits(childConstructor, parentConstructor){
var TempConstructor = function(){};
TempConstructor.prototype = parentConstructor.prototype; // Inherit parent prototype chain
childConstructor.prototype = new TempConstructor(); // Create buffer object to prevent assignments directly to parent prototype reference.
childConstructor.prototype.constructor = childConstructor; // Reset the constructor property back the the child constructor (currently set to TempConstructor )
};
var ParentConstructor = function(){
};
var ChildConstructor = function(){
ParentConstructor.call(this)
};
inherits(ChildConstructor, ParentConstructor);