Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   Магия с JS Proxy (https://javascript.ru/forum/misc/51934-magiya-s-js-proxy.html)

SunnyDay 26.11.2014 02:52

Магия с JS Proxy
 
При разработке фреймворка для XUL-UI (для Firefox плагинов) - встал вопрос о том, чтобы представлять элемент(там это чекбокс, селект, групбокс например) и группу тех-же элементов (массив тобишь) одним и тем же интерфейсом. При том что все элементы хоть и наследуются от от одного базового, но имеют разные прототипы(классы). То есть должны работать простые примеры:

element.attr('class','some');//устанавливает класс одного элемента в some
[element1, element2, element3].attr('class','some');//устанавливает класс 3-х элементов в some
//метода val - например нет в базовом классе этого фреймворка, потому что не все имеют значения - логично вроде
element.val();//возвращает значение элемента
[element1, element2, element3].val();//возвращает массив, каждый элемент которого соответствует значению каждого элемента в исходном массиве
[element1, element2].find('.some').attr('atr','atrVal');//в 2-х элементах находим все дочерние элементы с классом some и присваиваем им некий аттрибут


Заворачивать в один класс работу как со множеством, так и с одним элементов( как делает jQuery) - было бы накладно по архитектуре, и грозило бы потерей гибкости. Но оставлять как в mootools - аля забудь про множества, тоже неприемлемо. Очень быстро нагуглилось классное решение вопроса - Proxy https://developer.mozilla.org/en-US/..._Objects/Proxy . Создал обёрточный метод который при попытке вызова любого метода множества - вызывает его у каждого элемента множества, и формирует резутат-массив, каждый элемент которого соответствует результату вызванного множества:


//функция-проксер, проксирующая массив
var Multiplicity = function(in_array){
    var proxy = new Proxy(in_array,{
        get: function(target, name) {
          if  (name in target) return target[name];
          var proxyMethod = function(){
            var proxyResult = [];
            for(var i=0;i<target.length;i++){
                if (!target[i]) continue;
                if (typeof target[i][name] == 'function'){
                    proxyResult.push(target[i][name].apply(target[i], arguments));
                }else if (typeof target[i][name] != 'undefined'){
                    proxyResult.push(target[i][name]);
                }else{
                    proxyResult.push(undefined);
                }
            }
            return Multiplicity(proxyResult);
          }
          return proxyMethod;
        }
    });
    return proxy;
};

//ПРИМЕР использования проксера массивов
var cls = function(){
  this.f = function(){return new subCls();};
};
var subCls = function(){
  this.f2 = function(){return Math.random();};
};

Multiplicity([new cls(), new cls()]).f().f2();//выведет массив из двух случайных чисел
Multiplicity([new cls(), new subCls()]).f2();//выведет [undefined, случайное_число]


подход нравится своей волшебной гибкостью(с первого взгляда) - какие бы программисты не дописали методы в классы элементов, Proxy будет их "есть". Но вот вопрос, нет ли в таком подходе подводных камней, да таких что могут потопить UI-фреймворк? Есть ли причины не использовать Proxy кроме того что объект пока по-умолчанию доступен только в Firefox (а в остальных браузерах пока в стадии experimental)?

MallSerg 27.11.2014 02:20

мое скромное мнение

рони 27.11.2014 03:01

:write:
мне вот тут понравилось как одиночный элемент превращается в массив, да и сама обработка каждого элемента ...
http://javascript.ru/forum/misc/5188...tml#post342783

SunnyDay 07.01.2015 05:40

-не вот тут понравилось как одиночный элемент превращается в массив
так как там превращается - это стандартно, и везде используется. Но для случая - когда ко множеству нужно применить последовательно несколько методов в которых могут быть аргументы - так просто не получится. Вот пример jQuery:

jQuery('.red').find.('div').find('a').css('color','red');


Нужно чтобы работало так-же. Только с той разницей что в jQuery фиксированный класс элемента - не используется наследование, вместо этого плагинизация базового класса. Требуется же использовать наследование, да так что множеству(массиву состоящему возможно из элементов разных классов) можно так-же вызвать через точку методы, описанные хотя-бы в одном элементе множества.

Вобщем да, Proxy - пошло в разработку уже, решили его юзать :)

SunnyDay 07.01.2015 05:51

рони, кстати, вместо
isset(o, ['a', 'w', 'r', 't', 'y'])

можно по-человечески использовать

typeof o.a.w.r.t.y


если o наследник Proxy.

А так - как делает функция isset вышеприведённая, портит стилистику и читаемость кода.

Правка. нет - вру, typeof - не получится, но просто вместо эксепшена o.a.w.r.t.y может возвращить null например , если o - Proxy .

Erolast 07.01.2015 08:08

Цитата:

var cls = function(){
this.f = function(){return new subCls();};
};
var subCls = function(){
this.f2 = function(){return Math.random();};
}
Не делай так классы, у тебя каждому инстансу будет присуждаться собственный экземпляр метода, что перерасход памяти. Используй прототип:
var cls = function() {

}
cls.prototype.f = function() {
  return new subCls();
}

SunnyDay 13.01.2015 03:44

Цитата:

Сообщение от Erolast (Сообщение 350016)
Не делай так классы, у тебя каждому инстансу будет присуждаться собственный экземпляр метода, что перерасход памяти. Используй прототип:
var cls = function() {

}
cls.prototype.f = function() {
  return new subCls();
}

спасибо, в курсе. Пример просто лаконичный - с прототипами более громоздко.

SunnyDay 28.01.2015 03:04

Для информации - с JS-Proxy имеется всё-таки один подводный камень: это производительность. Видимо оптимизаторы пока его ещё не так лихо едят. Поясню - если завернуть массив в Proxy и переопределить get (как в примере топика), то в нём сразу же в огромные разы медленнее начинают работать вшитые методы, особенно indexOf . Поэтому не стоит это использовать Proxy больших массивов. Но для небольших массивов, которые не крутятся рекурсивно по 10 раз - самое то)


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