05.05.2011, 18:17
|
|
Интересующийся
|
|
Регистрация: 05.05.2011
Сообщений: 10
|
|
Классы и наследование
Описываю класс:
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" то есть свойство не передается.
Что я делаю не так?
Последний раз редактировалось Lerayne, 05.05.2011 в 18:20.
|
|
06.05.2011, 01:39
|
|
Рассеянный профессор
|
|
Регистрация: 06.04.2009
Сообщений: 2,379
|
|
Сообщение от Lerayne
|
after_cell
|
свойство создается в конструкторе класса, который ни разу не вызывается, т. е. after_cell не существует на момент вызова "new Topic". Используйте свойство prototype.
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();
Последний раз редактировалось Riim, 06.05.2011 в 01:45.
|
|
06.05.2011, 12:42
|
|
Интересующийся
|
|
Регистрация: 05.05.2011
Сообщений: 10
|
|
А что если в родительском классе есть не только объявление свойств и методов, но и некие действия со свойствами, конструктор так сказать?
Лично я решил проблему путем вызова Child.superclass.constructor.apply(this, arguments) внутри дочернего класса, но как-то ведь конструктор через прототип объявлять можно?
|
|
07.05.2011, 06:05
|
|
Рассеянный профессор
|
|
Регистрация: 06.04.2009
Сообщений: 2,379
|
|
Сообщение от Lerayne
|
я решил проблему путем вызова Child.superclass.constructor.apply(this, arguments) внутри дочернего класса
|
неверный подход, дочерний класс (да и кто угодно другой) может создавать экземпляр родительского, только если это нужно по смыслу, но не ради того что бы выдернуть из него какие-то значения. В конце концов, в конструкторе родительского класса может помимо вычислений нужных значений происходить что-то, что пока совсем не нужно (например, ajax-запрос).
Сообщение от Lerayne
|
что если в родительском классе есть не только объявление свойств и методов, но и некие действия со свойствами
|
тогда создаем метод выполняющий "некие действия со свойствами" и используемый в обоих конструкторах (дочерний класс унаследует его):
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();
|
|
07.05.2011, 20:57
|
Новичок на форуме
|
|
Регистрация: 19.02.2008
Сообщений: 9,177
|
|
Сообщение от Riim
|
неверный подход, дочерний класс (да и кто угодно другой) может создавать экземпляр родительского, только если это нужно по смыслу, но не ради того что бы выдернуть из него какие-то значения.
|
Нормальный подход. Здесь не создаётся экземпляр родительского класса, а просто вызывается его конструктор в контексте текущего this.
|
|
07.05.2011, 22:35
|
|
Интересующийся
|
|
Регистрация: 05.05.2011
Сообщений: 10
|
|
Спасибо, попробую оба варианта
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, 08.05.2011 в 00:56.
|
|
08.05.2011, 03:57
|
|
Рассеянный профессор
|
|
Регистрация: 06.04.2009
Сообщений: 2,379
|
|
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", "Заголовок!" и "Сообщение!"
};
Сообщение от Kolyaj
|
Здесь не создаётся экземпляр родительского класса, а просто вызывается его конструктор в контексте текущего this
|
все равно плохо.
Последний раз редактировалось Riim, 08.05.2011 в 04:05.
|
|
08.05.2011, 11:59
|
|
Интересующийся
|
|
Регистрация: 05.05.2011
Сообщений: 10
|
|
Спасибо. Особенно понравилось решение по перезаписи метода, которое я давно искал.
Насчет вашей реализации конструктора - я к сожалению еще не на том уровне, чтобы полностью понимать его, но вопрос - полностью ли корректна такая его реализация:
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, то как она работает?
|
|
08.05.2011, 13:12
|
|
Рассеянный профессор
|
|
Регистрация: 06.04.2009
Сообщений: 2,379
|
|
Сообщение от Lerayne
|
полностью ли корректна такая его реализация
|
вполне, только функция $extend создается при каждом вызове. Достаточно создавать один раз. Если она не нужна в глобальной области видимости, можно оформить замыканием:
var Class = (function() {
var $extend = function() {/* ..... */};
return function(parent, declaration) {
/* все как в твоем примере, только без $extend */
var Klass = function() {
this.initialize.apply(this, arguments);
}
/* ..... */
};
})();
Сообщение от Lerayne
|
имеет ли какое-то значение использование $ в названиях функций и свойств
|
нет. Используют, когда имя уже занято или просто для красоты. Иногда используют для смыслового отделения от контекста выполнения, например, $extend это тоже, что и window.$extend , если убрать $, получится window.extend и теперь, судя по имени, эта функция должна дописывать свойства в window, но она делает немного другое, вот я и добавил $.
Сообщение от Lerayne
|
что дает наличие Object.extend
|
Object.extend - это почти родное место для этой функции (в идеале конечно Object.prototype.extend в сочетании с флагом DontEnum, но этот флаг понимают только последние интерпретаторы, а без него будут проблемы: http://andir-notes.blogspot.com/2009...prototype.html). $extend нужен, потому что обращаться через Object.extend не очень удобно, а функция применяется часто.
Сообщение от Lerayne
|
Немного смутило свойство Klass.$super. Если это замена .superclass, то как она работает?
|
"super" - это ключевое слово, зарезервированное на будущее. В будущем (может быть) оно будет ссылкой на родительский класс. Что бы не использовать занятое слово добавляется $ или выбирается другое слово (superclass, parent и т . п.).
Последний раз редактировалось Riim, 08.05.2011 в 13:16.
|
|
25.05.2011, 03:56
|
|
Интересующийся
|
|
Регистрация: 05.05.2011
Сообщений: 10
|
|
Еще один вопрос (скорее просьба):
Можно ли изменить этот конструктор так, чтобы в методе класса-потомка инструкция "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;
};
})();
|
|
|
|