Классы и наследование
Описываю класс:
function Item (row, type) {
appendKids ( this.container = newel('div', type, type+'_'+row['id'])
, this.before_cell = newel('div', 'before_cell')
, this.data_cell = newel('div', 'data_cell')
, this.after_cell = newel('div', 'after_cell')
);
}
(newel - простенькая обертка для создания элемента с классом и ID, appendKids - функция, поочередно добавляющая к первому аргументу все последующие в качестве дочерних) Далее - обьявляю класс, который наследует первый:
function extend(Parent, Child) {
var F = function() { }
F.prototype = Parent.prototype
Child.prototype = new F()
Child.prototype.constructor = Child
Child.superclass = Parent.prototype
}
function Topic (row, type) {
var clickload = function(){
alert('test!');
}
this.after_cell.onclick = clickload;
}
extend(Item, Topic);
somevar = new Topic(row, type);
Получаю ошибку "this.after_cell is undefined" то есть свойство не передается. Что я делаю не так? |
Цитата:
function extend(Parent, Child) {
var F = function() { }
F.prototype = Parent.prototype
Child.prototype = new F()
Child.prototype.constructor = Child
Child.superclass = Parent.prototype
}
// у вас так:
var Parent = function() {
this.prop = 'Ok!';
};
var Child = function() {
alert(this.prop);
};
extend(Parent, Child);
new Child();
// а нужно так:
var Parent = function() {
};
Parent.prototype.prop = 'Ok!';
var Child = function() {
alert(this.prop);
};
extend(Parent, Child);
new Child();
|
А что если в родительском классе есть не только объявление свойств и методов, но и некие действия со свойствами, конструктор так сказать?
Лично я решил проблему путем вызова Child.superclass.constructor.apply(this, arguments) внутри дочернего класса, но как-то ведь конструктор через прототип объявлять можно? |
Цитата:
Цитата:
function extend(Parent, Child) {
var F = function() { }
F.prototype = Parent.prototype
Child.prototype = new F()
Child.prototype.constructor = Child
Child.superclass = Parent.prototype
}
var Parent = function() {
// Используем в родительском конструкторе
alert(this.method());
};
Parent.prototype.method = function() {
return 'Вычисляются какие-то значения';
};
var Child = function() {
// Используем в дочернем конструкторе
alert(this.method());
};
extend(Parent, Child);
new Child();
|
Цитата:
|
Спасибо, попробую оба варианта :)
UPD: Попробовал. Наткнулся на сложности с множественным наследованием. Приведу наглядный пример:
function extend(Parent, Child) {
var F = function() { }
F.prototype = Parent.prototype;
Child.prototype = new F();
Child.prototype.constructor = Child;
Child.superclass = Parent.prototype;
}
// функция создания дива с классом и содержимым
function div(className, content){
var elem = document.createElement('DIV');
if (className) elem.className = className;
if (content) elem.innerHTML = content;
return elem;
}
// начинаем экспереметировать с наследованием по варианту 1 (не используем прототип, используем конструктор суперкласса в начале каждого класса-потомка)
function Parent(color){
this.container = div('container', 'empty');
this.container.style.background = color;
}
extend(Parent, Child = function(){
Child.superclass.constructor.apply(this, arguments);
this.header = div('header', 'Заголовок!');
this.container.appendChild(this.header);
});
extend(Child, GrandChild = function(){
GrandChild.superclass.constructor.apply(this, arguments);
this.message = div('messge', 'Сообщение!');
this.container.appendChild(this.message);
});
// вариант 2, все свойства и методы добавляются через прототип:
function P(color){
this.populate(color);
}
P.prototype = {
populate: function(color){
this.container = div('container', 'empty');
this.container.style.background = color;
}
} // пока всё нормально
function C(color){
this.populate(color);
if (this.func) this.func();
}
C.prototype = {
func: function(){
this.header = div('header', 'Заголовок!');
this.container.appendChild(this.header);
}
}
extend(P, C); // populate - наследуется, а вот к func в конструкторе доступа нет
function GC(color){
this.populate(color);
if (this.func) this.func();
this.message = div('messge', 'Сообщение!');
this.container.appendChild(this.message);
}
extend(C, GC); // func по прежнему недоступна, сама функция работает потому что все действия затолканы в конструктор
window.onload = function(){
document.body.appendChild(new Parent('#7777FF').container); // выводит только "empty"
document.body.appendChild(new Child('#9999FF').container); // выводит "empty" и "Заголовок!"
document.body.appendChild(new GrandChild('#BBBBFF').container); // "empty", "Заголовок!" и "Сообщение!"
document.body.appendChild(new P('#FF7777').container); // выводит только "empty"
document.body.appendChild(new C('#FF9999').container); // выводит только "empty"
document.body.appendChild(new GC('#FFbbbb').container); // выводит "empty" и "Сообщение!"
}
|
Lerayne, у тебя 2 ошибки:
1. ты перезаписываешь все свойство prototype новым объектом (стр. 43, 54). В старом содержались всякие Function#apply и Function#call , а в новом их нет. Нужно просто дописывать новые методы в уже существующий объект. 2. функция extend тоже перезаписывает весь prototype, но делает это грамотно - новый объект содержит все нужные методы (наследует их от Function). Таким образом, ты сначала добавляешь новый метод, а затем в extend он затирается вместе со всем prototype. Поэтому нет доступа к func. Нужно сперва вызывать extend, а затем добавлять новые методы. Поправил твой пример:
function extend(Parent, Child) {
var F = function() { }
F.prototype = Parent.prototype;
Child.prototype = new F();
Child.prototype.constructor = Child;
Child.superclass = Parent.prototype;
}
// функция создания дива с классом и содержимым
function div(className, content){
var elem = document.createElement('DIV');
if (className) elem.className = className;
if (content) elem.innerHTML = content;
return elem;
}
// вариант 2, все свойства и методы добавляются через прототип:
function P(color){
this.populate(color);
}
P.prototype.populate = function(color){
this.container = div('container', 'empty');
this.container.style.background = color;
};
function C(color){
this.populate(color);
this.func();
}
extend(P, C);
C.prototype.func = function(){
this.header = div('header', 'Заголовок!');
this.container.appendChild(this.header);
};
function GC(color){
this.populate(color);
if (this.func) this.func();
this.message = div('messge', 'Сообщение!');
this.container.appendChild(this.message);
}
extend(C, GC);
window.onload = function() {
document.body.appendChild(new P('#FF7777').container); // выводит только "empty"
document.body.appendChild(new C('#FF9999').container); // выводит "empty" и "Заголовок!"
document.body.appendChild(new GC('#FFbbbb').container); // "empty", "Заголовок!" и "Сообщение!"
};
Что бы не делать таких ошибок, можно использовать небольшой конструктор классов. У меня бы получилось так:
var $extend = Object.extend = function(self, obj) {
if (self == null) self = {};
for (var key in obj) self[key] = obj[key];
return self;
}
var Class = {
create: function(parent, declaration) {
function klass() {
this.initialize.apply(this, arguments);
}
if (typeof parent == 'function') {
function F() {}
F.prototype = parent.prototype;
klass.prototype = new F();
} else {
if (parent != null) declaration = parent;
parent = Object;
}
$extend(klass.prototype, declaration).initialize || (klass.prototype.initialize = Function.blank);
klass.$super = parent;
klass.prototype.$super = parent.prototype;
return klass.prototype.constructor = klass;
}
};
function div(className, content) {
var elem = document.createElement('DIV');
if (className) elem.className = className;
if (content) elem.innerHTML = content;
return elem;
}
var P = Class.create({
initialize: function(color) {
this.populate(color);
},
populate: function(color) {
this.container = div('container', 'empty');
this.container.style.background = color;
}
});
var C = Class.create(P, {
populate: function(color) {
P.prototype.populate.call(this, color);
this.header = div('header', 'Заголовок!');
this.container.appendChild(this.header);
}
});
var GC = Class.create(C, {
populate: function(color) {
C.prototype.populate.call(this, color);
this.message = div('messge', 'Сообщение!');
this.container.appendChild(this.message);
}
});
window.onload = function() {
document.body.appendChild(new P('#FF7777').container); // выводит только "empty"
document.body.appendChild(new C('#FF9999').container); // выводит "empty" и "Заголовок!"
document.body.appendChild(new GC('#FFbbbb').container); // "empty", "Заголовок!" и "Сообщение!"
};
Цитата:
|
Спасибо. Особенно понравилось решение по перезаписи метода, которое я давно искал.
Насчет вашей реализации конструктора - я к сожалению еще не на том уровне, чтобы полностью понимать его, но вопрос - полностью ли корректна такая его реализация:
var Class = function(parent, declaration) {
var $extend = Object.extend = function(self, obj) {
if (self == null) self = {};
for (var key in obj) self[key] = obj[key];
return self;
}
var Klass = function() {
this.initialize.apply(this, arguments);
}
if (typeof parent == 'function') {
function F(){}
F.prototype = parent.prototype;
Klass.prototype = new F();
} else {
if (parent != null) declaration = parent;
parent = Object;
}
$extend(Klass.prototype, declaration).initialize || (Klass.prototype.initialize = Function.blank);
Klass.$super = parent;
Klass.prototype.$super = parent.prototype;
return Klass.prototype.constructor = Klass;
}
var Pa = Class({
initialize: function(color) {
this.populate(color);
},
populate: function(color) {
this.container = div('container', 'empty');
this.container.style.background = color;
}
});
var Ci = Class(Pa, {
populate: function(color) {
P.prototype.populate.call(this, color);
this.header = div('header', 'Заголовок!');
this.container.appendChild(this.header);
}
});
В примере, который мы рассматриваем моя реализация работает корректно, ошибок нее выдает, но не видите ли вы каких-то скрытых подводных граблей? И еще несколько вопросов: 1) имеет ли какое-то значение использование $ в названиях функций и свойств? Судя по тому что знаю я - значение у доллара чисто символическое, оно для программиста, а не для интерпретатора. 2) для чего нужно $extend = Object.extend = function, что дает наличие Object.extend? 3) Немного смутило свойство Klass.$super. Если это замена .superclass, то как она работает? |
Цитата:
var Class = (function() {
var $extend = function() {/* ..... */};
return function(parent, declaration) {
/* все как в твоем примере, только без $extend */
var Klass = function() {
this.initialize.apply(this, arguments);
}
/* ..... */
};
})();
Цитата:
Цитата:
Цитата:
|
Еще один вопрос (скорее просьба):
Можно ли изменить этот конструктор так, чтобы в методе класса-потомка инструкция "Child.superclass.prototype.methodName.apply(t his, arguments)" выглядела не так громоздко, но чтобы ее так же можно было вызывать в любом месте расширяемого метода дочернего класса? Чисто для эстетики кода? :)
var Class = (function() {
var extend = Object.extend = function(self, obj) {
if (self == null) self = {};
for (var key in obj) self[key] = obj[key];
return self;
}
return function(parent, declaration) {
var Klass = function() {
this.initialize.apply(this, arguments);
}
if (typeof parent == 'function') {
function F(){}
F.prototype = parent.prototype;
Klass.prototype = new F();
} else {
if (parent != null) declaration = parent;
parent = Object;
}
extend(Klass.prototype, declaration).initialize || (Klass.prototype.initialize = Function.blank);
Klass.superclass = parent;
Klass.prototype.superclass = parent.prototype;
return Klass.prototype.constructor = Klass;
};
})();
|
| Часовой пояс GMT +3, время: 20:48. |