Модераторы: Sardar, Aliance

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Наследование и состояние над-класса 
V
    Опции темы
Се ля ви
Дата 16.6.2008, 15:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Java/SOAрхитектор
****


Профиль
Группа: Модератор
Сообщений: 2016
Регистрация: 5.6.2004
Где: place without tim e and space

Репутация: 5
Всего: 127



Цитата(AKS @  13.6.2008,  06:03 Найти цитируемый пост)
Теперь единственное, что может напугать, так это: "подразумевается-то, что у каждого объекта по многу свойств". Т.е. если будет много св-в, то конструктор будет выглядеть громоздко. Но это, наверняка, можно обойти, используя, например, массивы или еще как-нибудь... 

Думаю, лучше всего решить это инструментальными средствами - в средах программирования или обфускатором-оптимизатором JavaScript-кода - надо только присмотреть наиболее удачный и расширяемый и дописать к нему эту функциональность. В итоге, я думаю, можно будет написать объект в простой схеме наследования и она автоматом при оптимизации преобразуется в лёгкую (про себя вторую я называю так).

Я уже продумываю этот момент smile


--------------------
  )
 (
[_])
проф. блог

Кролики думали, что занимаются любовью, а на самом деле их просто разводили...
PM MAIL WWW Skype GTalk   Вверх
Се ля ви
Дата 22.6.2008, 19:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Java/SOAрхитектор
****


Профиль
Группа: Модератор
Сообщений: 2016
Регистрация: 5.6.2004
Где: place without tim e and space

Репутация: 5
Всего: 127



Несколько улучшений для лёгкой модели:

1. Вызов метода init:
Код
        //Initialization
        if (arguments.length >= this.constructor.length)
            this.init.apply(this, arguments);

Т.е. я предлагаю считать заявленные непосредственно в конструкторе параметры обязательными, а все параметры сверх - необязательными и с этих позиций мы вызываем метод init, передавая ему все параметры, переданные конструктору, только в случае, если параметров передано не меньше, чем заявлено в объявлении конструктора.

2. В методе init вызов метода init для предка производим так:
Код
if (_super.init) _super.init.apply(this, arguments);
, соответственно, теперь можем его copy/paste`ить, ничего не меняя (теперь это может взять на себя среда разработки).

3. Избавляемся от постоянных строчек "this.privateState._get();" и "this.privateState._set();" в каждом public методе, необходимости всякий раз объявлять какую-либо переменную (например, "result") и присваивания ей значения private`ного поля перед его возвратом (для выполнения "this.privateState._set();"), а так же выбрасываем логику подсчёта вызовов в объекте privateState за счёт создания специального private-метода 
Код
var methodCaller = function(){
    this.privateState._get();
    
    /** @type {Object} */
    var result = methodCaller.caller.apply(this, arguments);
    
    this.privateState._set();
    
    return result;
}

и одной строчки вызова его в начале каждого public метода, которую предлагаю помещать сразу после открытия блока функции дабы она не мешалась с основной логикой метода: 
Код
this.getPrivateField1 = function() { if (!_super) return methodCaller.apply(this, arguments);
    return _privateField1;
}

, т.е. всю работу по взятию и сохранению данных из privateState за нас выполняет этот метод, а наличие или отсутствие экспортируемых данных определяется по переменной _super, которая должна быть у любого объекта всегда (если он ни от кого не наследуется, тогда она должна принимать значение Object.prototype:
Код
this.createPrivateState = function(super_) {
    return new function() {
        var __super = super_ || Object.prototype;
        //...
).

В общем, работающий код лёгкой модели:
Код
//light model of JavaScript Inheritance
var E = function() {
        /** @type {function} */ this.constructor = E;
        
        //Other logic...
    },
    F = function() {
    
        /** @type {function} */ this.constructor = F;
        
        if (this.constructor._class) {
            
            if (this.constructor._class.constructor)
                delete this.constructor._class.constructor;
            
            //Get privateState object
            this.privateState = this.constructor._class.createPrivateState(this.constructor.prototype);
            this.constructor.prototype = new this.constructor.prototype.constructor;
            
            //Load methods
            for (var m in this.constructor._class)
                if (typeof this.constructor._class[m] == 'function'
                    && m != 'createPrivateState')
                    
                    this[m] = this.constructor._class[m];
            
            //Initialization
            if (arguments.length >= this.constructor.length)
                this.init.apply(this, arguments);
            
        } else {
            
            //Inheritance
            this.constructor.prototype = new E;
            
            var _super,
                _privateField1,
                _privateField2;
            
            this.createPrivateState = function(super_) {
                return new function() {
                    var __super = super_ || Object.prototype,
                        __privateField1,
                        __privateField2;
                    
                    this._get = function() {
                        _super = __super;
                        _privateField1 = __privateField1;
                        _privateField2 = __privateField2;
                    };
                    
                    this._set = function() {
                        __super = _super;
                        __privateField1 = _privateField1;
                        __privateField2 = _privateField2;
                        
                        _super =
                        _privateField1 =
                        _privateField2 = null;
                    };
                };
            }
            
            var methodCaller = function(){
                this.privateState._get();
                
                /** @type {Object} */
                var result = methodCaller.caller.apply(this, arguments);
                
                this.privateState._set();
                
                return result;
            }
            
            this.init = function () { if (!_super) return methodCaller.apply(this, arguments);
                
                if (_super.init) _super.init.apply(this, arguments);
                
                //...
                
                delete this.init;
                return this;
            }
            
            this.getPrivateField1 = function() { if (!_super) return methodCaller.apply(this, arguments);
                return _privateField1;
            }
            
            this.setPrivateField1 = function(privateField1) { if (!_super) return methodCaller.apply(this, arguments);
                
                _privateField1 = privateField1;
                return this;
            }
            
            this.constructor._class = this;
            return new this.constructor();
        }
    };

//Test
var f1 = new F,
    f2 = new F;

alert('f1.setPrivateField1(2).getPrivateField1() = ' + f1.setPrivateField1(2).getPrivateField1()); //'f1.setPrivateField1(2).getPrivateField1() = 2'
alert('f2.setPrivateField1(3).getPrivateField1() = ' + f2.setPrivateField1(3).getPrivateField1()); //'f2.setPrivateField1(3).getPrivateField1() = 3'
alert('f1.getPrivateField1() = ' + f1.getPrivateField1()); //'f1.getPrivateField1() = 2'

alert('f1.setPrivateField1 === f2.setPrivateField1 = ' + (
    f1.setPrivateField1 === f2.setPrivateField1
));//'f1.setPrivateField1 === f2.setPrivateField1 = true'


И всё бы хорошо, но... есть проблема:
Код
alert('F._class instanceof F = ' + (
    F._class instanceof F
));//'F._class instanceof F = false'

alert('(new F) instanceof F = ' + (
    (new F) instanceof F
));//'(new F) instanceof F = false'

Т.е. теперь ни класс, ни объект не распознаются оператором instanceof как экземпляры конструктора F... Нехорошо... smile

У кого-нибудь есть по этому поводу какие-нибудь предложения?


--------------------
  )
 (
[_])
проф. блог

Кролики думали, что занимаются любовью, а на самом деле их просто разводили...
PM MAIL WWW Skype GTalk   Вверх
dsCode
Дата 22.6.2008, 22:45 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 565
Регистрация: 8.9.2007
Где: Saint-Petersburg

Репутация: 19
Всего: 26



Се ля ви, у вас там ошибка в строке "return new this.constructor().init.apply(null, arguments);" (FireBug выдает), поэтому пока особо не вникаю, пофиксите. По поводу instanceof (тоже при беглом просмотре) - в 18 строке (this.constructor.prototype = new this.constructor.prototype.constructor;) вы подменяете прототип конструктора. Поэтому (new F).__proto__ уже не сравнивается с  прототипом F, но сравнивается с прототип E (и даже прототипом Object):

Код


alert((new F) instanceof E); // true
alert((new F) instanceof Object); // true

alert((new F) instanceof F); // false


P.S.:

Цитата(Се ля ви @  22.6.2008,  19:08 Найти цитируемый пост)
//light model of JavaScript Inheritance


и все-таки (повторюсь в 10 раз - из-за особенностей языка), больше подошло бы "optimized heavy model of ..."



--------------------
the .code inside
:my music
PM MAIL WWW ICQ Jabber   Вверх
Се ля ви
Дата 22.6.2008, 23:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Java/SOAрхитектор
****


Профиль
Группа: Модератор
Сообщений: 2016
Регистрация: 5.6.2004
Где: place without tim e and space

Репутация: 5
Всего: 127



Цитата(dsCode @  22.6.2008,  22:45 Найти цитируемый пост)
return new this.constructor().init.apply(null, arguments);

Да, я уже пофиксил, просто не пойму, как можно конструктору объекта пареметры передать из arguments? При вызове обычной функции fnName.apply(this, arguments) работает, а вот если перед этим появляется ключевое слово new, возникает ошибка...


Цитата(dsCode @  22.6.2008,  22:45 Найти цитируемый пост)
По поводу instanceof (тоже при беглом просмотре) - в 18 строке (this.constructor.prototype = new this.constructor.prototype.constructor;) вы подменяете прототип конструктора. Поэтому (new F).__proto__ уже не сравнивается с  прототипом F, но сравнивается с прототип E (и даже прототипом Object)

Да, я как раз только что копал реализацию Rhino (это как раз реализация ScriptEngine от разработчиков проекта Mozilla). Нашёл вот это:
Код
    public boolean hasInstance(Scriptable instance)
    {
        Object protoProp = ScriptableObject.getProperty(this, "prototype");
        if (protoProp instanceof Scriptable) {
            return ScriptRuntime.jsDelegatesTo(instance, (Scriptable)protoProp);
        }
        throw ScriptRuntime.typeError1("msg.instanceof.bad.prototype",
                                       getFunctionName());
    }

, а jsDelegatesTo реализован вот как:
Код
    public static boolean jsDelegatesTo(Scriptable lhs, Scriptable rhs) {
        Scriptable proto = lhs.getPrototype();

        while (proto != null) {
            if (proto.equals(rhs)) return true;
            proto = proto.getPrototype();
        }

        return false;
    }

Мда... Но я не могу иначе - мне обязательно нужно создавать отдельный прототип для каждого объекта наследника, иначе как сделать так, что бы внутреннее состояние (private переменные) для каждого объекта наследника отличались?

Ладно, буду ещё думать...

Добавлено @ 23:27
Цитата(dsCode @  22.6.2008,  22:45 Найти цитируемый пост)
Цитата(Се ля ви @  22.6.2008,  19:08 Найти цитируемый пост)
//light model of JavaScript Inheritance


и все-таки (повторюсь в 10 раз - из-за особенностей языка), больше подошло бы "optimized heavy model of ..."

Хм, ну не знаю... Я хотел подчеркнуть, что одна модель простая - simple - т.е. лёгкая для понимания и модификации со стороны разработчика, а другая - light - лёгкая для браузера. А heavy - это как раз "тяжёлая", сразу интуитивно возникает вопрос - зачем она акая тяжёлая вообще нужна? И дискомфорт даже какой-то смутный...


--------------------
  )
 (
[_])
проф. блог

Кролики думали, что занимаются любовью, а на самом деле их просто разводили...
PM MAIL WWW Skype GTalk   Вверх
dsCode
Дата 23.6.2008, 13:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 565
Регистрация: 8.9.2007
Где: Saint-Petersburg

Репутация: 19
Всего: 26



Еще пару ошибок нашел:

- apply init'a не вызывался, потому что в 81 строе вы сами его уничтожаете (но дальше там опять ошибки пошли, после того, как init вызвался - в общем, надо еще смотреть, где там что теряется);

this.constructor.length всегда будет 0 (и что это вообще? когда это свойство будет ставиться?);


Да, instanceof в любом случае работает с прототипами конструкторов, а вы его (прототип) подменяете.


Цитата(Се ля ви @  22.6.2008,  23:19 Найти цитируемый пост)
А heavy - это как раз "тяжёлая"


так я и говорил с легким сарказмом (с легким! ;)), чтобы еще раз сказать, что JS и так-то в 50-200 раз медленней Си и Java в виду своей нынешней ООП-модели и интерпретируемости, а тут он еще медленней становится (ну понятно, что для обеспечения привычного вида инкапсуляции, но я убежден, что в нынешней реализации JS это излишне). Поэтому все-таки это "хэви", но уже в разы оптимизированная  (в виду хранения гетторов / сеттеров в одном объекте) "хэви" (что, естественно, радует).

Это сообщение отредактировал(а) dsCode - 23.6.2008, 13:15


--------------------
the .code inside
:my music
PM MAIL WWW ICQ Jabber   Вверх
Се ля ви
Дата 23.6.2008, 14:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Java/SOAрхитектор
****


Профиль
Группа: Модератор
Сообщений: 2016
Регистрация: 5.6.2004
Где: place without tim e and space

Репутация: 5
Всего: 127



Цитата(dsCode @  23.6.2008,  13:11 Найти цитируемый пост)
- apply init'a не вызывался, потому что в 81 строе вы сами его уничтожаете (но дальше там опять ошибки пошли, после того, как init вызвался - в общем, надо еще смотреть, где там что теряется);

Да, надо ещё тестировать, но чесно говоря, уже лень - я альтернативную модель сейчас делаю, с тем, что бы всё-таки заюзать прототипы вместо классов. А эту реализацию развивать смысла нет, потому что instanceof, на мой взгляд, терять нельзя. К тому же действительно новая модель получается более элегантной smile

Скоро всё будет.

Цитата(dsCode @  23.6.2008,  13:11 Найти цитируемый пост)
- this.constructor.length всегда будет 0 (и что это вообще? когда это свойство будет ставиться?);

Я написал достаточно подробно про эту фичу:
Цитата(Се ля ви @  22.6.2008,  19:08 Найти цитируемый пост)
1. Вызов метода init:
Код
        //Initialization
        if (arguments.length >= this.constructor.length)
            this.init.apply(this, arguments);

Т.е. я предлагаю считать заявленные непосредственно в конструкторе параметры обязательными, а все параметры сверх - необязательными и с этих позиций мы вызываем метод init, передавая ему все параметры, переданные конструктору, только в случае, если параметров передано не меньше, чем заявлено в объявлении конструктора.

Дело в том, что я этот код пишу не только для демонстрации, но и для copy/paste`а, так что многое может показаться излишним, просто задача решается в общем виде. this.constructor.length - это количество параметров инициализации, передаваемых конструктору (который сейчас выполняется) - у любой функции есть такой метод. В данном случае это - ноль, но если кто-то будет использовать этот код, у него наверняка параметры будут и это не будет ноль, так что ноль вместо этой конструкции я поставить не могу.

Цитата(dsCode @  23.6.2008,  13:11 Найти цитируемый пост)
Да, instanceof в любом случае работает с прототипами конструкторов, а вы его (прототип) подменяете.

Да, "понимание приходит с опытом"... Жаль. Придётся существенно переделывать модель. Но у меня есть ощущение, что она от этого только улучшится и упростится smile
Цитата(dsCode @  23.6.2008,  13:11 Найти цитируемый пост)
Цитата(Се ля ви @  22.6.2008,  23:19 Найти цитируемый пост)
А heavy - это как раз "тяжёлая"


так я и говорил с легким сарказмом (с легким! ;)), чтобы еще раз сказать, что JS и так-то в 50-200 раз медленней Си и Java в виду своей нынешней ООП-модели и интерпретируемости, а тут он еще медленней становится (ну понятно, что для обеспечения привычного вида инкапсуляции, но я убежден, что в нынешней реализации JS это излишне). Поэтому все-таки это "хэви", но уже в разы оптимизированная  (в виду хранения гетторов / сеттеров в одном объекте) "хэви" (что, естественно, радует).

Хорошо, убедили. Пускай будет Heavy JavaScript inheritance realization (HJSI) и Light JavaScript inheritance realization (LJSI)smile


--------------------
  )
 (
[_])
проф. блог

Кролики думали, что занимаются любовью, а на самом деле их просто разводили...
PM MAIL WWW Skype GTalk   Вверх
dsCode
Дата 23.6.2008, 14:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 565
Регистрация: 8.9.2007
Где: Saint-Petersburg

Репутация: 19
Всего: 26



Цитата(Се ля ви @  23.6.2008,  14:09 Найти цитируемый пост)
я альтернативную модель сейчас делаю, с тем, что бы всё-таки заюзать прототипы вместо классов. А эту реализацию развивать смысла нет, потому что instanceof, на мой взгляд, терять нельзя. К тому же действительно новая модель получается более элегантной smile

Скоро всё будет.


угу, с удовольствием посмотрю (кстати, вот моя старая реализация, имитирующая классовое ООП в JS (с наследованием, тыры-пырами и т.д.). Там есть недочеты, но это старый код (сейчас уже переписан); основные моменты для работы там реализованы (по крайней мере, мне хватало, чтобы сделать систему объектов и связать их. Хотя, если честно, все это тоже накладывает расходы ресурсов).

С this.constructor.length я и так все понял до этого, я просто спрашивал - когда он установится? -

Цитата(Се ля ви @  23.6.2008,  14:09 Найти цитируемый пост)
у любой функции есть такой метод


и метод и свойтво =) а я вот запамятовал )) (мне причудилось, что кол-во аргументов можно только из arguments посмотреть)

Это сообщение отредактировал(а) dsCode - 23.6.2008, 14:48


--------------------
the .code inside
:my music
PM MAIL WWW ICQ Jabber   Вверх
Се ля ви
Дата 24.6.2008, 00:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Java/SOAрхитектор
****


Профиль
Группа: Модератор
Сообщений: 2016
Регистрация: 5.6.2004
Где: place without tim e and space

Репутация: 5
Всего: 127



Итак, представляю вашему вниманию тяжёлую модель реализации наследования. Это не конечная реализация, она нуждается в некотором улучшении и, возможно, дополнении. Но пока она представляет вполне работающий вариант.

В основе лежит следующая схема:
Цитата
Constructors    |    Prototypes       |      Objects
   .
   .
  _._                __________                _____
 | C |--prototype-->(     C    )<--__proto__--(new C)
 |___|<-constructor-(.prototype)                 |
   |                      |                      |
_parent               __proto__               _super
   |                      |                      |
  \|/                ____\|/___                _\|/_
 | B |--prototype-->(     B    )<--__proto__--(new B)
 |___|<-constructor-(.prototype)                 |
   |                      |                      |
_parent               __proto__               _super
   |                      |                      |
  \|/                ____\|/___                _\|/_
 | A |--prototype-->(     A    )<--__proto__--(new A)
 |___|<-constructor-(.prototype)
   |                      |
_parent               __proto__
   |                      |
  \|/                ____\|/___
 |Obj|--prototype-->(  Object  )
 |ect|<-constructor-(.prototype)
                          |
                      __proto__
                          |
                         \|/
                         null

При этом методы хранятся в прототипах, им же передаются privateState`ы от объектов во время выполнения их методов по уже показанному ранее механизму - в этом плане ничего не изменилось, просто вместо явной ссылки _class теперь используется неявная ссылка __proto__.

Что же касается объекта-предка, то теперь он доступен только по private ссылке _super у каждого объекта. При этом специальный блок инициализатора объекта (он находится в методе createPrivateState) присваивает ссылки на все его не перекрываемые открытые поля и функции, создаваемому объекту (при этом мы специально передаём ссылки на методы объекту, а не прототипу для того, что бы иметь возможность применять эту модель не только для конструкторов, в которых уже реализована эта модель, но и для любых других - что бы не было необходимости переписывать уже созданный код).

К сожалению, модель ещё менее удобна для copy/paste`а - как вы сейчас увидите, в ней в нескольких местах придётся проставлять имя объекта, от которого мы наследуемся и имя самого объекта, а так же, что является старой проблемой, в нескольких местах необходимо дублировать имена private полей. Над этим я сейчас работаю.

Итак, пока прошу любить и жаловать:
Код
// Heavy JavaScript inheritance realization (HJSI)

var A = function(x) { this.x = x; },
    B = function(x, y, z) {
        
     if (!this.constructor._parent && this.constructor == B) {
         
         /**
          * Note: if have no parent constructor then you shoud appropriate link to Object
          * @type {function}
          */
            this.constructor._parent = A;
            
            /** @type {Object} */
            this.constructor.prototype = this.constructor._parent.prototype;
            this.constructor.prototype = new this.constructor;
            return new this.constructor(x, y, z);
        }

        if (this.constructor == A) {
            
            this.constructor = B;
            
            //Class methods & private fields
            var methodCaller = function() {
                this.privateState._get();
                
                /** @type {Object} */
                var result = methodCaller.caller.apply(this, arguments);
                
                this.privateState._set();
                
                return result;
            }
            
            var _super,
                _privateField1,
                _privateField2;
            
            this.createPrivateState = function(super_, args) {
                
                //Linking methods and fields from super object with this object
                for (var m in super_)
                    if (super_.hasOwnProperty(m)
                        && !(
                             this.constructor.prototype.hasOwnProperty(m)
                             || this.hasOwnProperty(m)
                            )
                       )
                            this[m] = super_[m];
                
                //Private fields initialization
                _super = super_;
                _privateField1 = x;
                _privateField2 = y;
                
                delete args;
                
                return (new function() {
                    var __super,
                        __privateField1,
                        __privateField2;
                    
                    this._get = function() {
                        _super = __super;
                        _privateField1 = __privateField1;
                        _privateField2 = __privateField2;
                        
                        if (_super.privateState) _super.privateState._get();
                    };
                    
                    this._set = function() {
                        __super = _super;
                        __privateField1 = _privateField1;
                        __privateField2 = _privateField2;
                        
                        if (_super.privateState) _super.privateState._set();
                        
                        _super =
                        _privateField1 =
                        _privateField2 = null;
                        
                        return this;
                    };
                })._set();
            }
            
            this.getPrivateField1 = function() { if (!_super) return methodCaller.apply(this, arguments);
                return _privateField1;
            }
            
            this.setPrivateField1 = function(privateField1) { if (!_super) return methodCaller.apply(this, arguments);
                
                _privateField1 = privateField1;
                return this;
            }
            
        } else {
            
            //Public state fields declaration
            this.publicField1 = z;
            //...
            
            //Create private fields conteiner for this object
            this.privateState = this.createPrivateState(new this.constructor._parent(x), x, y);
        }
        
    }


//Test:
var f1 = new B(1,2,3),
    f2 = new B(4,5,6);

alert('f1.setPrivateField1(2).getPrivateField1() = ' +
        f1.setPrivateField1(2).getPrivateField1()
     );//'f1.setPrivateField1(2).getPrivateField1() = 2'

alert('f2.setPrivateField1(3).getPrivateField1() = ' +
      f2.setPrivateField1(3).getPrivateField1()
     );//'f2.setPrivateField1(3).getPrivateField1() = 3'

alert('f1.getPrivateField1() = ' +
      f1.getPrivateField1()
     );//'f1.getPrivateField1() = 2'

alert('f1.x = ' + f1.x);//'f1.x = 1'

alert('f1.setPrivateField1 === f2.setPrivateField1 = ' + (
    f1.setPrivateField1 === f2.setPrivateField1
));//'f1.setPrivateField1 === f2.setPrivateField1 = true'

alert('f1.publicField1 = ' +
      f1.publicField1
     );//'f1.publicField1 = 3'

alert('(new B) instanceof B = ' + (
    (new B) instanceof B
));//'(new B) instanceof B = true'

alert('(new B) instanceof A = ' + (
    (new B) instanceof A
));//'(new B) instanceof A = true'



--------------------
  )
 (
[_])
проф. блог

Кролики думали, что занимаются любовью, а на самом деле их просто разводили...
PM MAIL WWW Skype GTalk   Вверх
Се ля ви
Дата 24.6.2008, 10:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Java/SOAрхитектор
****


Профиль
Группа: Модератор
Сообщений: 2016
Регистрация: 5.6.2004
Где: place without tim e and space

Репутация: 5
Всего: 127



И лёгкая модель, с которой работает операция instanceof:
Код
// Light JavaScript inheritance realization (LJSI)

var A = function(x) { this.x = x; },
    B = function(x, y, z) {
        
     if (!this.constructor._parent && this.constructor == B) {
         
         /**
          * Note: if have no parent constructor then you shoud appropriate link to Object
          * @type {function}
          */
            this.constructor._parent = A;
            
            /** @type {Object} */
            this.constructor.prototype = this.constructor._parent.prototype;
            this.constructor.prototype = new this.constructor;
            return new this.constructor(x, y, z);
        }

        if (this.constructor == A) {
            this.constructor = B;
            return this;
        }
        
            
        //Fields declaration
        var _super = new this.constructor._parent(x),//You must initialize _super object first! 
            _privateField1 = x,
            _privateField2 = y;
        this.publicField1 = z;
        //...
        
        //Methods
        this.getPrivateField1 = function() {
            return _privateField1;
        }
        
        this.setPrivateField1 = function(privateField1) {
            
            _privateField1 = privateField1;
            return this;
        }
        //...
        
        
        //Linking methods and fields from super object with this object
        for (var m in _super)
            if (_super.hasOwnProperty(m)
                && !(
                     this.constructor.prototype.hasOwnProperty(m)
                     || this.hasOwnProperty(m)
                    )
               )
                    this[m] = _super[m];
            
    }


//Test:
var f1 = new B(1,2,3),
    f2 = new B(4,5,6);

alert('f1.setPrivateField1(2).getPrivateField1() = ' +
        f1.setPrivateField1(2).getPrivateField1()
     );//'f1.setPrivateField1(2).getPrivateField1() = 2'

alert('f2.setPrivateField1(3).getPrivateField1() = ' +
      f2.setPrivateField1(3).getPrivateField1()
     );//'f2.setPrivateField1(3).getPrivateField1() = 3'

alert('f1.getPrivateField1() = ' +
      f1.getPrivateField1()
     );//'f1.getPrivateField1() = 2'

alert('f1.x = ' + f1.x);//'f1.x = 1'

alert('f1.setPrivateField1 === f2.setPrivateField1 = ' + (
    f1.setPrivateField1 === f2.setPrivateField1
));//'f1.setPrivateField1 === f2.setPrivateField1 = false'

alert('f1.publicField1 = ' +
      f1.publicField1
     );//'f1.publicField1 = 3'

alert('(new B) instanceof B = ' + (
    (new B) instanceof B
));//'(new B) instanceof B = true'

alert('(new B) instanceof A = ' + (
    (new B) instanceof A
));//'(new B) instanceof A = true'

Как видим, результат идентичный кроме строчки вывода 'f1.setPrivateField1 === f2.setPrivateField1 = false', в которой мы чётко видим, что теперь у каждого объекта свои методы, которые при увеличении их количества гораздо быстрее будут забивать всю память, чем просто данные. Тем не менее, эта модель проще и удобнее для написания библиотек руками.

В будущем я думаю создать специальный инструментарий как расширение к компрессору (пока первый кандидат на расширение - YUI Compressor, хотя предложения принимаются - для обсуждения этого вопроса создана другая тема), который будет преобразовывать LJSI-конструкторы в HJSI, позволяя разработчику не думать о сложностях реализации HJSI-модели. Ну и, безусловно, время от времени будут вноситься изменения с целью упростить обе модели.

Так же на очереди:
  • Реализация пакетов и динамического импорта (Constructor loading) конструкторов на основе AJAX
  • Реализация интерфейсов в Java-стиле
  • Иерархия Exception`онов
  • Реализация аннотаций для JavaScript
  • Реализация основных pattern`ов на JS
  • Разбор и демонстрация применения близких к Java проектов на JavaScript - Log4JavaScriptJSUnitJSDoc и др.
Правда, это всё будет возможно лишь в случае, если руководство холдинга Ланит и дальше будет благосклонно смотреть на мои исследования и по факту спонсировать их, не сильно загружая меня работой, при этом платя з/п smile


--------------------
  )
 (
[_])
проф. блог

Кролики думали, что занимаются любовью, а на самом деле их просто разводили...
PM MAIL WWW Skype GTalk   Вверх
dsCode
Дата 24.6.2008, 12:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 565
Регистрация: 8.9.2007
Где: Saint-Petersburg

Репутация: 19
Всего: 26



Щас времени нет особо, позже напишу.

Цитата(Се ля ви @  24.6.2008,  00:47 Найти цитируемый пост)
// Heavy


Се ля ви, я ж саркастически утрировал (писал уже об этом) =)) Зачем такое название? (просто как-то буквально вы это название восприняли) )) Сами говорили:

Цитата(Се ля ви @  22.6.2008,  23:19 Найти цитируемый пост)
сразу интуитивно возникает вопрос - зачем она акая тяжёлая вообще нужна? И дискомфорт даже какой-то смутный... 



--------------------
the .code inside
:my music
PM MAIL WWW ICQ Jabber   Вверх
dsCode
Дата 24.6.2008, 23:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 565
Регистрация: 8.9.2007
Где: Saint-Petersburg

Репутация: 19
Всего: 26



Цитата(Се ля ви @  24.6.2008,  00:47 Найти цитируемый пост)
this.constructor.prototype = this.constructor._parent.prototype;
            this.constructor.prototype = new this.constructor;


хех =) поначалу хотел спросить, зачем двойное присвоение this.constructor.prototype (строки 15, 16), потом увидел, что первый для ветки if (this.constructor == A) { ... }

Есть проблема "холостых выстрелов" (поставьте alert(...) в конструкторе B и создайте один объект - по вашей схеме проалертит три раза, в то время как конструктор должен срабатывать при создании пользовательского объекта (а не вспомогательных того же "класса" внутри реализации библиотеки)):

Код


B = function(x, y, z) {
  alert('?')
  ...
}

var f1 = new B(1, 2, 3);



Вижу (строка 50), что проперти "класса" А копируются в создаваемый объект - т.е. возьмутся не из прототипного объекта (который должен быть экземпляром "класса" А (ну, или wrapper'ом - для избежания "холостых выстрелов")), а будут ownProperty.

В общем, сказать, что это перегруженная и сложная для понимания модель - это практически ничего не сказать (уж не обессудьте)! =))  Да я и изначально говорил, что из-за одного спортивного интереса сымитировать концепции Java, так усложнять и напрягать JavaScript не стоит) =))

Это сообщение отредактировал(а) dsCode - 24.6.2008, 23:43


--------------------
the .code inside
:my music
PM MAIL WWW ICQ Jabber   Вверх
Се ля ви
Дата 27.6.2008, 15:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Java/SOAрхитектор
****


Профиль
Группа: Модератор
Сообщений: 2016
Регистрация: 5.6.2004
Где: place without tim e and space

Репутация: 5
Всего: 127



Цитата(dsCode @  24.6.2008,  23:39 Найти цитируемый пост)
хех =) поначалу хотел спросить, зачем двойное присвоение this.constructor.prototype (строки 15, 16), потом увидел, что первый для ветки if (this.constructor == A) { ... }

Да. Сейчас я немного исправил - я убрал оттуда ссылку на предка и поставил проверку на равенство реально конструктора свойству "constructor" - так по крайней мере не придётся несколько раз упоминать в коде имя конструктора-предка.
Но всё равно остаётся проблема, что при copy/paste придётся вписывать имя конструктора в трёх местах, что не очень удобно. При чём, что самое неприятное - от этого не удаётся избавиться даже в лёгкой модели... :(
Буду ещё думать.

Цитата(dsCode @  24.6.2008,  23:39 Найти цитируемый пост)
Есть проблема "холостых выстрелов" (поставьте alert(...) в конструкторе B и создайте один объект - по вашей схеме проалертит три раза, в то время как конструктор должен срабатывать при создании пользовательского объекта (а не вспомогательных того же "класса" внутри реализации библиотеки))

Почему же, никаких холостых выстрелов! При вызове первый раз конструктор тягается 3 раза.
  • Первый раз - собственно, вызов пользователем библиотеки:
    Код
    var f1 = new B(1,2,3);
  • Второй раз - в 48-й строчке - создание "правильного" - т.е. нужного в контексте данной задачи - прототипа:
    Код
    this.constructor.prototype = new this.constructor;
  • Третий - 49-я строка - собственно, создание экземпляра уже с правильным прототипом:
    Код
    return new this.constructor(x, y, z);
При последующем создании объектов при помощи этого конструктора он вызывается один раз. Всё чётко по модели, как и задумывалось - никакой проблемы.


Цитата(dsCode @  24.6.2008,  23:39 Найти цитируемый пост)
Вижу (строка 50), что проперти "класса" А копируются в создаваемый объект - т.е. возьмутся не из прототипного объекта (который должен быть экземпляром "класса" А (ну, или wrapper'ом - для избежания "холостых выстрелов")), а будут ownProperty.

Да. Этим я как раз и добиваюсь того, что бы у каждого объекта-потомка был свой объект предка и при изменении состояния предка (private-полей) они менялись только для данного объекта, а у остальных таких же объектов объекты предков оказывались бы "не повреждёнными".

Этим действием я добиваюсь, что все открытые (интерфейсные) элементы предка получает потомок, но не через прототип, а напрямую. Дело в том, что эта модель наследования должна уметь работать не только с конструкторами, сделанными по ней же, но и с любыми другими альтернативными моделями наследования и даже простыми конструкторами без наследования, где и методы, как и public-поля могут оказаться частью объекта, а не прототипа. По-этому, раз использовать экземпляр предка в качестве прототипа я не могу, мне нужно подтягивать их напрямую smile


--------------------
  )
 (
[_])
проф. блог

Кролики думали, что занимаются любовью, а на самом деле их просто разводили...
PM MAIL WWW Skype GTalk   Вверх
Се ля ви
Дата 28.6.2008, 00:23 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Java/SOAрхитектор
****


Профиль
Группа: Модератор
Сообщений: 2016
Регистрация: 5.6.2004
Где: place without tim e and space

Репутация: 5
Всего: 127



Удалось избавиться от упоминаний предка и данного класса во многих местах - теперь и то и другое упоминается лишь в одном месте, таким образом copy/paste`ить стало земетно легче.

Код
// Heavy JavaScript inheritance realization (HJSI)

/** Sheme of Inheritance:
 * 
 *|Constructors|       Prototypes        |      Objects     |
 *   .
 *   .
 *  _._                __________                _____
 * | C |--prototype-->(     C    )<--__proto__--(new C)
 * |___|<-constructor-(.prototype)                 |
 *   |                      |                      |
 *_parent               __proto__               _super
 *   |                      |                      |
 *  \|/                ____\|/___                _\|/_
 * | B |--prototype-->(     B    )<--__proto__--(new B)
 * |___|<-constructor-(.prototype)                 |
 *   |                      |                      |
 *_parent               __proto__               _super
 *   |                      |                      |
 *  \|/                ____\|/___                _\|/_
 * | A |--prototype-->(     A    )<--__proto__--(new A)
 * |___|<-constructor-(.prototype)
 *   |                      |
 *_parent               __proto__
 *   |                      |
 *  \|/                ____\|/___
 * |Obj|--prototype-->(  Object  )
 * |ect|<-constructor-(.prototype)
 *                          |
 *                      __proto__
 *                          |
 *                         \|/
 *                         null
 */


var A = function(x) { this.x = x; },
    B = function(x, y, z) {
    
        
     if (this.constructor._child) {
         /**------------------------------------------------------------------------------ 
             * Prototype crearation begin ---------------------------------------------------
             * ------------------------------------------------------------------------------ */
            
            this.constructor = this.constructor._child;
            delete this.constructor._parent._child;
            
            //Methods & private fields declaration section
            
            /**
             * Support method for call other methods of class in instances
             * context (values of private fields)
             * 
             * @return {Object} result of executed the method, wich call this one, in this object context.  
             */
            var methodCaller = function() {
                
                if (!this.privateState
                    || typeof this.privateState != 'object'
                    || !this.privateState._get
                    || !this.privateState._set)
                            throw new NullPointerException(
                                'The \'privateState\' field has invalid format or does not exists.'
                            );
                
                
                this.privateState._get();
                
                if (!_super)
                    throw new NullPointerException('The \'privateState\' object is not correct.');
                
                
                /** @type {Object} */
                var result = methodCaller.caller.apply(this, arguments);
                
                this.privateState._set();
                
                return result;
            }
            
            var /** {Object} */ _super,
                /** {number} */ _privateField1,
                /** {number} */ _privateField2;
            
            /**
             * Method for create the privateState object
             * 
             * @protected
             * 
             * @param {Object} super_ - object, created by parent constructor
             * @param {Object} args - array or pseudo array (arguments) object, contained init params for private fields
             */
            this.createPrivateState = function(super_, args) {
                
                if (this.createPrivateState.caller != this.constructor)
                    throw new FieldAccessException (
                        'Attemption to call the protected method createPrivateState of constructor \''
                        + this.constructor.constructorName
                        + '\' prototype!'
                    );
                
                if (this.privateState)
                    throw new FieldAccessException (
                        'Attemption to call the method createPrivateState of constructor \''
                        + this.constructor.constructorName
                        + '\' prototype from object, witch already have the \'privateState\' field!'
                    );
                
                //Block the possibility of call this method by this object again
                this.createPrivateState = null;
                
                //Linking methods and fields from super object with this object
                for (var m in super_)
                    if (super_.hasOwnProperty(m)
                        && !( this.constructor.prototype.hasOwnProperty(m)
                              || this.hasOwnProperty(m) )
                       )
                            this[m] = super_[m];
                
                //Private fields initialization section
                _super = super_;
                _privateField1 = x;
                _privateField2 = y;
                
                delete args;
                
                return (new function() {
                    var __super,
                        __privateField1,
                        __privateField2;
                    
                    this._get = function() {
                        _super = __super;
                        _privateField1 = __privateField1;
                        _privateField2 = __privateField2;
                        
                        if (_super.privateState) _super.privateState._get();
                    };
                    
                    this._set = function() {
                        __super = _super;
                        __privateField1 = _privateField1;
                        __privateField2 = _privateField2;
                        
                        if (_super.privateState) _super.privateState._set();
                        
                        _super =
                        _privateField1 =
                        _privateField2 = null;
                        
                        return this;
                    };
                })._set();
            }
            
            this.getPrivateField1 = function() { if (!_super) return methodCaller.apply(this, arguments);
                return _privateField1;
            }
            
            this.setPrivateField1 = function(privateField1) { if (!_super) return methodCaller.apply(this, arguments);
                
                _privateField1 = privateField1;
                return this;
            }
            //..
            
            
            /**------------------------------------------------------------------------------ 
             * Prototype crearation end -----------------------------------------------------
             * ------------------------------------------------------------------------------ */
     } else
         
         if (this.constructor._parent) {
         
             /**------------------------------------------------------------------------------ 
                 * Instance crearation begin ----------------------------------------------------
                 * ------------------------------------------------------------------------------ */
                
                //Public state fields declaration
                this.publicField1 = z;
                //...
                
                
                //Create private fields conteiner for this object
                this.privateState = this.createPrivateState(new this.constructor._parent(x), x, y);
                
                /**------------------------------------------------------------------------------
                 * Instance creation end --------------------------------------------------------
                 * ------------------------------------------------------------------------------ */
                
         } else {
         
             /** First constructor call. Making structure of inheritance. */
             
             
             /**
              * Note: if have no parent constructor then you shoud appropriate link to Object
              * @type {function}
              */
                this.constructor._parent = A;
                
                this.constructor.constructorName = 'B';
                
                this.constructor._parent._child = eval(this.constructor.constructorName)
                
                /** @type {Object} */
                this.constructor.prototype = this.constructor._parent.prototype;
                this.constructor.prototype = new this.constructor;
                
                return new this.constructor(x, y, z);
            }
        
    }


Пока не решены проблема с несколькими упоминаниями private-полей и вызовом конструктора из функции с теми параметрами, которые были переданы этой функции (т.е. метод apply для конструктора).


--------------------
  )
 (
[_])
проф. блог

Кролики думали, что занимаются любовью, а на самом деле их просто разводили...
PM MAIL WWW Skype GTalk   Вверх
АндрейМиндубаев
Дата 16.8.2008, 01:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 5
Регистрация: 14.8.2008
Где: Нижний Новгород

Репутация: нет
Всего: нет



Се ля ви, если интересно, то я здесь у себя в блоге написал про свой вариант реализации наследования.

ЗЫЖ Я читал этот форум в своём RSS-Reader-е, эта темка что-то остановилась - и я решил поделится своей разработкой.
PM MAIL WWW Jabber   Вверх
Се ля ви
Дата 19.8.2008, 13:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Java/SOAрхитектор
****


Профиль
Группа: Модератор
Сообщений: 2016
Регистрация: 5.6.2004
Где: place without tim e and space

Репутация: 5
Всего: 127



Цитата(АндрейМиндубаев @  16.8.2008,  01:15 Найти цитируемый пост)
Я читал этот форум в своём RSS-Reader-е, эта темка что-то остановилась


АндрейМиндубаев, данная конкретная тема не столько остановилась, сколько довольно-таки естественным путём завершилась, перейдя в новое качество. Дело в том, что мне стала очевидна излишняя громоздкость своей модели наследования и все попытки упростить её носят по сути косметический характер. Так что результатом данного исследования я считаю выработку адекватной модели наследования с использованием всех возможностей инкапсуляции JavaScript, вот только для эффективного использования данной модели на практике нужна одна из трёх вещей:
1. Либо нужно радикальным образом её упростить.
2. Либо нужно создать инструментальные средства, позволяющие упростить разработку с учётом этой модели.
3. Либо нужно создать интерпретатор в JS из другого языка - в моём случае лучше всего подходит Java.

В принципе, я сейчас работаю по всем трём направлениям.

По первому пути я копаюсь в спеке и изучаю тонкости JS, постоянно думая о том, как их применить для упрощения данной модели.

По второму - я, с одной стороны, разбираюсь с проектом YUI Compressor, который хочу приспособить для аналога компиляции JS. С другой стороны, продумываю новые элементы языка, которые можно было бы внести и на стадии такой компиляции превращать в более громоздкие конструкции, примеры которых вы можете видеть в этой теме. И с третьей стороны я ищу open-source редакторы, в которых можно было бы эту функциональность привнести наиболее эффективным образом - с подсветкой, подсказками и прочим. К сожалению, подающий надежды в этом плане SPKet IDE не является open-source проектом... :(

Ну и по третьему - уже, в общем-то, есть Google Web Toolkit. Однако, на мой взгляд, он слишком перегружен и я продумываю, как можно было бы легковесно реализовать многие вещи. Вот, например, со дня на день выложу сюда (отдельной темой) свою реализацию emum`ов как в Java - сейчас последние штрихи доделываю.

В общем, процесс идёт. Главное - есть интерес к созданию удобного и лаконичного инструментария для создания RIA. А всё остальное - приложится smile


--------------------
  )
 (
[_])
проф. блог

Кролики думали, что занимаются любовью, а на самом деле их просто разводили...
PM MAIL WWW Skype GTalk   Вверх
Страницы: (4) Все 1 2 [3] 4 
Ответ в темуСоздание новой темы Создание опроса
Форум для вопросов, которые имеются в справочниках, но их поиск вызвал затруднения, или для разработчика требуется совет или просьба отыскать ошибку. Напоминаем: 1) чётко формулируйте вопрос, 2) приведите пример того, что уже сделано, 3) укажите явно, нужен работающий пример или подсказка о том, где найти информацию.
 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | JavaScript: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.1546 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.