 
			
				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;
	};
})();
 
		
	
		
		
		
		
		
		
	
		
		
	
	
	 | 
 
 
	 
		 | 
 
 
 
 |  
  |