Классовый мутатор Binds.

Автор оригинала: Jan Kassens
  • Перевод
В обсуждении недавнего топика хабрасообщество заинтересовалось подробностями написания классов для MooTools и, в частности, мутаторами. В связи с этим мне захотелось что-нибудь написать на эту тему, пока не наткнулся на статью одного из разработчиков MooTools. В этом топике привожу перевод статьи, в которой Jan Kassens описывает пример использования классовых мутаторов.

Во время написания класса вам может понадобиться привязать метод класса к событию в качастве обработчика и использовать this внутри него в качестве экземпляра класса. Вы можете сказать, что это не проблема при использовании bind(). Все это верно, но ровно до тех пор, пока дело не доходит до удаления обработчика события, т.к. Function::bind() работает так, что возвращает новую функцию, оборачивающую оригинал. Поэтому Вам нужно где-то сохранить ту самую обертку, чтобы удалить обработчик (без удаления всех обработчиков событий элемента, что не очень хорошо внутри переносимого класса).

Примечание автора перевода. Автор статьи имеет в виду случай, когда обработчик события назначается с помощью el.addEvent('click', myFn.bind(this)). Таким образом его нельзя удалить с помощью el.removeEvent('click', myFn.bind(this)), потому что bind() вернет уже новую функцию, не ту что была использована в addEvent(). Единственным в данном случае решением будет удаление всех обработчиков элемента с помощью el.removeEvents('click'), но это затронет все обработчики, включая те, которые могли быть назначены сторонними классами.

Что я видел несколько раз — это объект, содержащий все привязанные функции, который создается в конструкторе, как видно из примера:
var MyClass = new Class({
  initialize: function(){
   this.bound = {
     sayHello: this.sayHello.bind(this),
     sayGoodbye: this.sayGoodbye.bind(this)
   }
   // Далее используем только this.bound.sayHello и
   // this.bound.sayGoodbye внутри класса
  },

  sayHello: function() { /* ... */ },
  sayGoodbye: function() { /* ... */ }
});

Мне такое решение не понравилось, потому что оно слишком громоздко: зачем мне использовать this.bound.myFn всегда, когда я пользуюсь привязанными функциями? И, потом, я не хочу каждый раз привязывать эти функции вручную. После рассмотрения различных решений я пришел к одному, оптимальному по скорости (не бойтесь, это быстрее, чем решение выше) и удобству.

Это — новый, так называемый «классовый мутатор» Binds. Многие из вас, возможно, не слышали о классовых мутаторах раньше, но точно их использовали при написании своих классов. Встроенные мутаторы: Implements и Extends, а вот и код Binds:
Class.Mutators.Binds = function(self, methods) {
  $splat(methods).each(function(method){
   var fn = self[method];
   self[method] = function(){
     return fn.apply(self, arguments);
   };
  });
};

Этот мутатор переопределяет все методы, переданные в него в строках новыми версиями, привязанными к экземпляру. Binds: 'foo' просто привяжет foo к классу, Binds: ['foo', 'bar'] привяжет foo и bar. Довольно просто, не правда ли?

Теперь давайте посмотрим, насколько упростится класс с использованием мутатора Binds:
var MyClass = new Class({
  Binds: ['sayHello', 'sayGoodbye'],

  initialize: function(){
   // Теперь используем только this.sayHello и
   // this.sayGoodbye внутри класса
  },

  sayHello: function() { /* ... */ },
  sayGoodbye: function() { /* ... */ }
});

Надеюсь, вы немного разобрались и сможете найти этому применение.
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 11

    +2
    Элегантненько. Вот за возможность таких решений, четкую нотацию и продуманность лично мне и нравится этот фреймворк. Не столь популярен как jQuery, но, черт возьми, как просто в нем реализуются многие аспекты кодинга — блеск! За статью спасибо, обязательно буду иметь ввиду.
      0
      Спасибо за все расширяемости JavaScript.
        0
        Если бы он избавился от статуса IE Killer, тогда он был бы пожалуй даже лучше jQuery.
        • НЛО прилетело и опубликовало эту надпись здесь
            0
            В каждой шутке есть доля правды. И эта правда довольно сильно мешает разрабатывать приложения =\
            У Mootools и правда есть проблемы с IE.
              0
              Просветите, пожалуйста.
          0
          Именно. Любимый фрейм.
          0
          Я конечно не разбираюсь в mt, только начал осваивать. Но что-то мне подсказывает, что в этом примере оно должно вести себя немного иначе :) Проблема с биндами появляется при наследовании…

          var Mutators = new Class({
          Implements: Options,
          Binds: 'sayBug',
          options: {
          variable: 'bug'
          },
          initialize: function(opt) {
          this.setOptions(opt);
          },
          sayBug: function() {
          console.log(this.options.variable);
          }
          });

          var MutatorsBug = new Class({
          Extends: Mutators,
          initialize: function(opt) {
          this.parent(opt);
          },
          })

          var xx = new MutatorsBug({variable: 'ok'});
          xx.sayBug();

            0
            когда работаем с его родителем, то всё ок…
            var yy = new Mutators({variable: 'ok'});
            yy.sayBug();
              +1
              так происходит, потому что this в sayBug указывает в другое место. А точнее this==instance, где instance из Class.Mutators.Extends:

              Extends: function(self, klass){
              var instance = new klass($empty);
              delete instance.parent;
              delete instance.parentOf;
              ...
              –1
              Извините, но это похоже на костыль, или просто пример какой-то неудачный.

              Чем не угодили замыкания? С их помощью можно организовать объекто-классы, и добиться вожделенного^Wжелаемого ООП. (Против ООП ничего не имею, — только против его неправильного использования.)

              События и обработчики — это тоже костыли, увы. %)

              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

              Самое читаемое