Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   Объекты, прототипы и циклы (https://javascript.ru/forum/misc/32156-obekty-prototipy-i-cikly.html)

bFree 05.10.2012 10:39

Объекты, прототипы и циклы
 
Здравствуйте!
Решил применять прототипное наследование. Столкнулся с не совсем очевидным для меня косяком. Сразу приведу иллюстрирующий пример кода:

// Базовый объект
function Foo(options) {
    this.setOptions(options);
}

Foo.prototype.defaultOptions = {
    some_option: null
};

Foo.prototype.setOptions = function(options) {
    this.options = jQuery.extend(true, this.defaultOptions, options);
};


// Наследник
function Bar(options) {
    this.setOptions(options);
}
extend(Bar, Foo);

// Добавим какие-то свои опции
Bar.prototype.defaultOptions = {
    some_option   : null,
    another_option: false
};


// Создадим несолько объектов
for(var i = 0; i < 4; i++) {
    var obj = new Bar({
        some_option: i,
        another_option: true
    });

    console.log(obj.options); // откроем каждую строку в Firebug'е и везде some_option будет равно 3 =(
}


В примере я использовал функцию extend из уроков с javascript.ru. Вот она:
function extend(Child, Parent) {
    var F = function() {};
    F.prototype = Parent.prototype;
    Child.prototype = new F();
    Child.prototype.constructor = Child;
    Child.superclass = Parent.prototype;
}


Если кто-нибудь сможет подсказать, в чем косяк и где я туплю, то буду очень признателен. Не раз выручали меня уже на этом форуме.

bFree 08.10.2012 09:56

Кажется, что никт оне поможет =(
Уже который день бьюсь и вообще не понимаю, что я делаю не так

Maxmaxmахimus 08.10.2012 11:36

во первых сделай то же самое алертом

alert( obj.options.some_option )




во вторых для наследования используй общеизвестную функцию Class, и с ней твой код будет выглядить так
п.с. в this.__construct__ описывается конструктор

// родитель
var Foo = new Class( function () {

	this.__construct__ = function ( options ) {
		 this.options = jQuery.extend( true, this.defaultOptions, options );
	};

	this.defaultOptions = {
		some_option:'foo_defoult'
	};

} );



// наследник ( в настеднике пишем только различия от родителя, либо добавляем что-то новое )
var Bar = new Class( Foo, function () {

	this.defaultOptions = {
		some_option:'bar_defoult'
	};

} );



и собственно сама функция

function Class( a, b ) {
	var description = b || a,
		superClass = b ? a : null,
		overname = Class.overname || 'super',
		Constructor = (description.name)
			? eval( "(function " + description.name +
			"(){ if ( this.__construct__ )  return this.__construct__.apply( this, arguments )})" )
			: function () {
			if ( this.__construct__ )  return this.__construct__.apply( this, arguments )
		},
		Object = function Object() {
		};

	Object.prototype = superClass ? superClass.prototype : Class.prototype;
	description.prototype = new Object;
	Constructor.prototype = new description( Constructor, description.prototype );

	var obj = Constructor.prototype;
	for ( var key in obj ) {
		if ( obj.hasOwnProperty( key ) && obj[key] instanceof Function ) {
			var parentProperty = description.prototype[key];
			if ( parentProperty )
				(function ( originalMethod, parentMethod, key ) {
					obj[key] = function () {
						var bk = this[overname];
						this[overname] = parentMethod;
						var returns = originalMethod.apply( this, arguments );
						if ( bk ) this[overname] = bk;
						else delete this[overname];
						return returns;
					}
				})( obj[key], parentProperty, key )
		}
	}

	Constructor.create = function ( args ) {
		function Object() {
			if ( this.__construct__ )  return this.__construct__.apply( this, args )
		}

		Object.prototype = Constructor.prototype;
		return new Object
	};

	return Constructor;
}


Где то был топик рассказывающий как её использовать, поищу, если не найду новый напишу.

bFree 08.10.2012 14:46

Спасибо за наводку!
Появились вопросы:
Почему лучше использовать alert (кроме того, что он прерывает работу скрипта)?

Почему лучше использовать Class ? Все, что я читал про наследование в JS сводилось к тому, что незачем пытаться эмулировать классы, как в других ЯП, а лучше использовать прототипы, ибо для этого язык и задуман.


Также, к сожалению, код работать не начал =(
// Базовый объект
            var Foo = new Class(function(options) {
                this.__construct__ = function(options) {
                    this.setOptions(options);
                };

                this.defaultOptions = {
                    some_option: null
                };

                this.setOptions = function(options) {
                    this.options = jQuery.extend(true, this.defaultOptions, options);
                };
            });

            // Наследник
            var Bar = new Class(Foo, function(options) {
                this.defaultOptions = {
                    some_option   : null,
                    another_option: false
                }
            });

            var objs = [];

            // Создадим несолько объектов
            for(var i = 0; i < 4; i++) {
                objs.push(new Bar({
                    some_option: i,
                    another_option: true
                }));
            }

            console.log(objs[1].options); // some_option = 3  (почему!?)
            console.log(objs[3].options); // some_option = 3 (так и должно быть)

Maxmaxmахimus 08.10.2012 14:57

Цитата:

Сообщение от bFree
Почему лучше использовать alert

Потому что консоль группирует однотипные выводы не проверяя особо их структуру, так и тут она подумала что эти обьекты одинаковые. и обьединила в один не смотря на то что значения свойств у них разные. это типа оптимизация у неё такая. алерт просто показал РЕАЛЬНОЕ положение вешей.


Цитата:

Сообщение от bFree
Почему лучше использовать Class ?

Функция класс не эмулирует ООП поведение других языков, и названа так лишь по аналогии. Её можно назвать и Extends. Она является лишь удобной оберткой над прототипным наследованием. Седня напишу топик про неё (модераторы удалили видимо случайно)


Цитата:

Сообщение от bFree
Также, к сожалению, код работать не начал =(

Твой код и работал до этого, прблема в оптимизации логера консоли. о чем я уже говорил выше)

иными словами логер глючный



П.С метод setOptions не обязательно было лписывать отдельно))) можно было прямо в констуркторе описать то что он делает)) потому что конструктор тоже наследуется (как ты заметил).
Метод который вызывается при создании обьектов (this.__construct__) наследуется дочерним "классам" ))
.ну да ладно, это кому как удобнее ))

bFree 08.10.2012 15:02

Maxmaxmахimus, спасибо за разъяснение!

Но, к сожалению, с алертами ситуация не исправилась =(
var objs = [];

// Создадим несолько объектов
for(var i = 0; i < 4; i++) {
    objs.push(new Bar({
        some_option: i,
        another_option: true
    }));
}

alert(objs[1].options.some_option); // some_option  = 3  =(
alert(objs[3].options.some_option); // some_options = 3

Maxmaxmахimus 08.10.2012 15:05

да что ты будешь делать )) щас напишу

bFree 08.10.2012 15:07

Цитата:

П.С метод setOptions не обязательно было лписывать отдельно))) можно было прямо в констуркторе описать то что он делает)) потому что конструктор тоже наследуется (как ты заметил).
Метод который вызывается при создании обьектов (this.__construct__) наследуется дочерним "классам" ))
.ну да ладно, это кому как удобнее ))
Это сделал для того, чтобы во внешнем коде мог менять опции.
Да и это просто пример. Понятное дело, что в setOptions могут быть какие-то объемные вещи, которые незачем пихать в конструктор.

bFree 08.10.2012 15:11

Ииии.. ваш вариант так же не работает =(

Maxmaxmахimus 08.10.2012 15:12

секунду))

Maxmaxmахimus 08.10.2012 15:20

нашел, проблема в
jQuery.extend(true, this.defaultOptions, options);

jQuery.extend изменяет и возвращает this.defaultOptions.

таким образом мы всем присваиваем в свойство this.options ссылку на один и тот же обьект this.defaultOptions)))))

лол можешь проверить

objs[1].options === objs[3].options //true
objs[2].options  === objs[1].defaultOptions //true

bFree 08.10.2012 15:29

Обоже мой. Я уже запарился перечитывать про прототипы, думал, что где-то в prototype пересекаются ссылки.

Оказалось, что я не умею читать документацию к jquery. Огромное спасибо!

Maxmaxmахimus 08.10.2012 15:31

используй лучше это)))


function extendObject( parent, child ) {
	function F (){}
	F.prototype = parent;

	var instance = new F;
	for ( var key in child )
	  instance[key] = child[key];

	return instance
}


за место
jQuery.extend(true, this.defaultOptions, options);


пиши

extendObject( this.defaultOptions, options );


______________________________________
п.с. если бы не существовало ишака, то функция extendObject выглядела бы вообще так
function extendObject (){
    child.__proto__ = parent;
    return child;
}

и работала бы в сотни раз быстрее.
но мир не без ослов))

bFree 08.10.2012 16:39

А как можно при помощи Class вызвать конструктор родителя?
В Coffescript есть вызов "super", а тут есть такое?

Maxmaxmахimus 08.10.2012 23:02

Есть конечно)) эту функцию писали профессионалы javascript)

this.super() внутри дочернего метода вызовет перекрытый мтеод родителя

function Class(g,h){var d=h||g,a=h?g:null,e=Class.overname||"super",f=function(){if(this.__construct__)return this.__construct__.apply(this,arguments)},i=function(){};i.prototype=a?a.prototype:Class.prototype;d.prototype=new i;f.prototype=new d(f,d.prototype);var c=f.prototype,b;for(b in c)c.hasOwnProperty(b)&&c[b]instanceof Function&&(a=d.prototype[b])&&function(b,d,a){c[a]=function(){var a=this[e];this[e]=d;var c=b.apply(this,arguments);a?this[e]=a:delete this[e];return c}}(c[b],a,b);return f};







var Animal = new Class( function () {
	this.run = function () {
		alert( 'this run!' )
	};
} );



var Cat = new Class( Animal, function () {
	this.run = function (){
		this.super(); // вызываем родительский перекрытый
		alert('...and jump!' ) // добавляем коту дополнительный функционал
	};
} );



var cat = new Cat;
cat.run(); // 'this run!'   // '...and jump!'


п.с. вот это вот слово super можно задавать в Class.overname = 'trololo' если слово super не устраивает))

И кстати

cat instanceof Cat    // true
cat instanceof Animal // true
cat instanceof Class  // true


А так же

Class.prototype.qq = 11;
Animal.prototype.ww = 77;

cat.qq // 11
cat.ww // 77

ну ты понял ;)

я же говорю это обычная обертка над прототипным наследованием)))) просто дико удобная

Maxmaxmахimus 09.10.2012 02:55

На самом деле у этой функции еще есть много фишек, все сразу и не опишешь)

bFree 09.10.2012 11:05

Отлично. Это то что нужно. Спасибо!

Maxmaxmахimus 09.10.2012 17:56

Да а еще можно статические свойства добавлять) например так



function Class(g,h){var d=h||g,a=h?g:null,e=Class.overname||"super",f=function(){if(this.__construct__)return this.__construct__.apply(this,arguments)},i=function(){};i.prototype=a?a.prototype:Class.prototype;d.prototype=new i;f.prototype=new d(f,d.prototype);var c=f.prototype,b;for(b in c)c.hasOwnProperty(b)&&c[b]instanceof Function&&(a=d.prototype[b])&&function(b,d,a){c[a]=function(){var a=this[e];this[e]=d;var c=b.apply(this,arguments);a?this[e]=a:delete this[e];return c}}(c[b],a,b);return f};






var Cat = new Class( function ( static ) {

	// static ссылается на то, что возвращает функция Class (на то что потом кладется в Cat)
	static.count = 0; // статическое свойство count 

	this.__construct__ = function () {
		static.count++
	};
} );


// теперь в Cat лежит класс котов, и через этот класс можно получить доступ к статическим свйоствам ни создав ни одного кота

alert( Cat.count ) //0

new Cat;
new Cat;
new Cat;

alert( Cat.count ) //3

Hekumok 10.10.2012 11:14

Maxmaxmахimus, когда снова создашь топик с этими классами?

nerv_ 10.10.2012 11:47

Цитата:

Сообщение от Hekumok
Maxmaxmахimus, когда снова создашь топик с этими классами?

Maxmaxmахimus, видишь, народ требует хлеба и зрелищ :)

Hekumok 10.10.2012 11:55

Цитата:

Сообщение от nerv_
Maxmaxmахimus, видишь, народ требует хлеба и зрелищ :)

Лол

Maxmaxmахimus 10.10.2012 12:35

Завтра или седня ночью планировал, устроим fallout )

Hekumok 10.10.2012 13:33

Цитата:

Сообщение от Maxmaxmахimus
Завтра или седня ночью планировал, устроим fallout )

:)

Hoshinokoe 11.10.2012 01:40

Цитата:

Сообщение от Maxmaxmахimus
нашел, проблема в
jQuery.extend(true, this.defaultOptions, options);

jQuery.extend изменяет и возвращает this.defaultOptions.

Неверное использование метода, документация.
Первый параметр - это объект, в который будем мержить остальные. Т.е. в данном случае должно быть так:
var options = jQuery.extend({}, this.defaultOptions, options);

Или, если мержить рекурсивно:
var options = jQuery.extend(true, {}, this.defaultOptions, options);

Maxmaxmахimus 11.10.2012 01:48

Мне кажется подключать jquery для такого ткого это бред

Hoshinokoe 11.10.2012 01:54

Maxmaxmахimus,
Согласен, но люди даже не могут правильно воспользоваться готовым методом... чего ж ожидать, если на чистом js будут писать

Maxmaxmахimus 11.10.2012 01:57

Ну он то вроде шарит в программировании а тут просто доки не прочитал))

bFree 11.10.2012 16:14

Hoshinokoe, Я написал, что невнимательно прочел документацию. Глаз замылился и не заметил. Успокойся.

Maxmaxmахimus, jquery подключен во всем проекте и с домом работают через него, поэтому тут тоже использовался. Проект достаточно большой.


Часовой пояс GMT +3, время: 21:57.