Объекты, прототипы и циклы
Здравствуйте!
Решил применять прототипное наследование. Столкнулся с не совсем очевидным для меня косяком. Сразу приведу иллюстрирующий пример кода: // Базовый объект 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; } Если кто-нибудь сможет подсказать, в чем косяк и где я туплю, то буду очень признателен. Не раз выручали меня уже на этом форуме. |
Кажется, что никт оне поможет =(
Уже который день бьюсь и вообще не понимаю, что я делаю не так |
во первых сделай то же самое алертом
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; } Где то был топик рассказывающий как её использовать, поищу, если не найду новый напишу. |
Спасибо за наводку!
Появились вопросы: Почему лучше использовать 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 (так и должно быть) |
Цитата:
Цитата:
Цитата:
иными словами логер глючный П.С метод setOptions не обязательно было лписывать отдельно))) можно было прямо в констуркторе описать то что он делает)) потому что конструктор тоже наследуется (как ты заметил). Метод который вызывается при создании обьектов (this.__construct__) наследуется дочерним "классам" )) .ну да ладно, это кому как удобнее )) |
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 |
да что ты будешь делать )) щас напишу
|
Цитата:
Да и это просто пример. Понятное дело, что в setOptions могут быть какие-то объемные вещи, которые незачем пихать в конструктор. |
Ииии.. ваш вариант так же не работает =(
|
секунду))
|
нашел, проблема в
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 |
Обоже мой. Я уже запарился перечитывать про прототипы, думал, что где-то в prototype пересекаются ссылки.
Оказалось, что я не умею читать документацию к jquery. Огромное спасибо! |
используй лучше это)))
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; } и работала бы в сотни раз быстрее. но мир не без ослов)) |
А как можно при помощи Class вызвать конструктор родителя?
В Coffescript есть вызов "super", а тут есть такое? |
Есть конечно)) эту функцию писали профессионалы 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 ну ты понял ;) я же говорю это обычная обертка над прототипным наследованием)))) просто дико удобная |
На самом деле у этой функции еще есть много фишек, все сразу и не опишешь)
|
Отлично. Это то что нужно. Спасибо!
|
Да а еще можно статические свойства добавлять) например так
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 |
Maxmaxmахimus, когда снова создашь топик с этими классами?
|
Цитата:
|
Цитата:
|
Завтра или седня ночью планировал, устроим fallout )
|
Цитата:
|
Цитата:
Первый параметр - это объект, в который будем мержить остальные. Т.е. в данном случае должно быть так: var options = jQuery.extend({}, this.defaultOptions, options); Или, если мержить рекурсивно: var options = jQuery.extend(true, {}, this.defaultOptions, options); |
Мне кажется подключать jquery для такого ткого это бред
|
Maxmaxmахimus,
Согласен, но люди даже не могут правильно воспользоваться готовым методом... чего ж ожидать, если на чистом js будут писать |
Ну он то вроде шарит в программировании а тут просто доки не прочитал))
|
Hoshinokoe, Я написал, что невнимательно прочел документацию. Глаз замылился и не заметил. Успокойся.
Maxmaxmахimus, jquery подключен во всем проекте и с домом работают через него, поэтому тут тоже использовался. Проект достаточно большой. |
Часовой пояс GMT +3, время: 21:57. |