Классы и наследование
Описываю класс:
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, время: 11:37. |