Javascript.RU

Создать новую тему Ответ
 
Опции темы Искать в теме
  #1 (permalink)  
Старый 05.05.2011, 18:17
Аватар для Lerayne
Интересующийся
Отправить личное сообщение для Lerayne Посмотреть профиль Найти все сообщения от Lerayne
 
Регистрация: 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.
Ответить с цитированием
  #2 (permalink)  
Старый 06.05.2011, 01:39
Аватар для Riim
Рассеянный профессор
Отправить личное сообщение для Riim Посмотреть профиль Найти все сообщения от Riim
 
Регистрация: 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.
Ответить с цитированием
  #3 (permalink)  
Старый 06.05.2011, 12:42
Аватар для Lerayne
Интересующийся
Отправить личное сообщение для Lerayne Посмотреть профиль Найти все сообщения от Lerayne
 
Регистрация: 05.05.2011
Сообщений: 10

А что если в родительском классе есть не только объявление свойств и методов, но и некие действия со свойствами, конструктор так сказать?

Лично я решил проблему путем вызова Child.superclass.constructor.apply(this, arguments) внутри дочернего класса, но как-то ведь конструктор через прототип объявлять можно?
Ответить с цитированием
  #4 (permalink)  
Старый 07.05.2011, 06:05
Аватар для Riim
Рассеянный профессор
Отправить личное сообщение для Riim Посмотреть профиль Найти все сообщения от Riim
 
Регистрация: 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();
Ответить с цитированием
  #5 (permalink)  
Старый 07.05.2011, 20:57
Новичок на форуме
Отправить личное сообщение для Kolyaj Посмотреть профиль Найти все сообщения от Kolyaj
 
Регистрация: 19.02.2008
Сообщений: 9,177

Сообщение от Riim
неверный подход, дочерний класс (да и кто угодно другой) может создавать экземпляр родительского, только если это нужно по смыслу, но не ради того что бы выдернуть из него какие-то значения.
Нормальный подход. Здесь не создаётся экземпляр родительского класса, а просто вызывается его конструктор в контексте текущего this.
Ответить с цитированием
  #6 (permalink)  
Старый 07.05.2011, 22:35
Аватар для Lerayne
Интересующийся
Отправить личное сообщение для Lerayne Посмотреть профиль Найти все сообщения от Lerayne
 
Регистрация: 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.
Ответить с цитированием
  #7 (permalink)  
Старый 08.05.2011, 03:57
Аватар для Riim
Рассеянный профессор
Отправить личное сообщение для Riim Посмотреть профиль Найти все сообщения от Riim
 
Регистрация: 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.
Ответить с цитированием
  #8 (permalink)  
Старый 08.05.2011, 11:59
Аватар для Lerayne
Интересующийся
Отправить личное сообщение для Lerayne Посмотреть профиль Найти все сообщения от Lerayne
 
Регистрация: 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, то как она работает?
Ответить с цитированием
  #9 (permalink)  
Старый 08.05.2011, 13:12
Аватар для Riim
Рассеянный профессор
Отправить личное сообщение для Riim Посмотреть профиль Найти все сообщения от Riim
 
Регистрация: 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.
Ответить с цитированием
  #10 (permalink)  
Старый 25.05.2011, 03:56
Аватар для Lerayne
Интересующийся
Отправить личное сообщение для Lerayne Посмотреть профиль Найти все сообщения от Lerayne
 
Регистрация: 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;
	};

})();
Ответить с цитированием
Ответ



Опции темы Искать в теме
Искать в теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Javascript - классы, они есть или их нету в Javascript? Golovastik Javascript под браузер 39 15.02.2013 10:38
Множественное наследование - миф или реальность?! B~Vladi Оффтопик 109 04.09.2012 16:13
Наследование класса-замыкания и вызов метода предка BobiKK Общие вопросы Javascript 2 21.03.2010 13:26
Наследование Fliand Events/DOM/Window 0 12.08.2009 20:48
наследование установок динамически создаваемыми элементами majestic jQuery 1 15.09.2008 13:21