Binds Class Mutator

When writing a Class you often want to bind a class method to an event and access this as the instance of the class. You could say this isn't a big deal using .bind(). That's right until it comes to removing the event again, since Function::bind works in that way that it returns a new function wrapping the old one. Hence you need to store that very function when you want to remove it again (without removing all events from that element, which isn't good inside a portable class).

What I've seen a couple of times is an object holding all bound functions and created in the constructor, example:

  1. var MyClass = new Class({
  2.  
  3. initialize: function(){
  4. this.bound = {
  5. sayHello: this.sayHello.bind(this),
  6. sayGoodbye: this.sayGoodbye.bind(this)
  7. }
  8. // now only using this.bound.sayHello and
  9. // this.bound.sayGoodbye inside the class
  10. },
  11.  
  12. sayHello: function() { /* ... */ },
  13.  
  14. sayGoodbye: function() { /* ... */ }
  15.  
  16. });

I wasn't quite happy with this solution, because it's too verbose: why always use this.bound.myFn when I always want the bound one? Secondly, I don't want to do the binding of all these functions by hand. After some discussions on different solutions this is what I come up with as the optimum between speed (don't worry its faster than the solution above) and usability.

A new so called "class mutator" named Binds. Most of you probably didn't hear of class mutators before, but you sure have used a class mutator before when you've written one or another class. Built in mutators are Implements and Extends and here is the code of the Binds mutator:

  1. Class.Mutators.Binds = function(self, methods) {
  2.  
  3. $splat(methods).each(function(method){
  4. var fn = self[method];
  5. self[method] = function(){
  6. return fn.apply(self, arguments);
  7. };
  8. });
  9.  
  10. };

What it does is overriding all methods passed to it as a string with a version bound to the instance. Binds: 'foo' would just bind foo to the class, Binds: ['foo', 'bar'] binds foo and bar. Pretty simple, huh?

Now let's see how the class can be simplified using the Binds mutator:

  1. var MyClass = new Class({
  2.  
  3. Binds: ['sayHello', 'sayGoodbye'],
  4.  
  5. initialize: function(){
  6. // now only using this.sayHello and
  7. // this.sayGoodbye inside the class
  8. },
  9.  
  10. sayHello: function() { /* ... */ },
  11.  
  12. sayGoodbye: function() { /* ... */ }
  13.  
  14. });

I hope you learned a bit again or can find use of it.

If you have any wishes or ideas for further articles let me know.


10 Responses to Binds Class Mutator

  1. 1 Rendez 2008-06-30 09:37:59

    Great Jan, I'm glad you started this blog. I see where 1.2 is being extremely different. Many of us use set and get a lot already, but you sharing you own techniques on them would be nice.

    Cheers!

  2. 3 Thomas Aylott 2008-06-30 13:57:48

    Ok. That's pretty slick.
    I'll take 1 please, with fries.

  3. 4 Class::Binds Mutator » Clientside 2008-07-01 20:29:31

    ... showDigg.delay(50); } window.addEvent('domready', showDigg); Jan Kassens, a MooTools contributor, has posted a nifty little trick to help you automatically bind methods to a class. Usually when we reference a method of a class when adding an event, we bind “this” to ...

  4. 9 Cohen 2008-07-04 14:34:17

    Some questions regarding this...
    Why not use:

    var MyClass = new Class({
    initialize: function(){
    },
    sayHello: function() { /* ... */ }.bind(this),
    sayGoodbye: function() { /* ... */ }.bind(this)
    });

    it's shorter (don't need an mutator), faster(no loop) and uses less code (depending on the amount of methods)? And is better readable for others (less cryptic, especially for people who don't now about mutators)

  5. 10 kassens 2008-07-04 17:07:41

    @Cohen: you're solution doesn't work, since you don't have the instance of the class, when you create the class. You could override the methods inside initialize, like: this.sayHello = this.sayHello.bind(this).

    Amount of code: It's a small difference in size (might be slightly bigger or smaller), so it shouldn't count. What should count is the readability and reliability of the code, which I think is improved in many/most cases with the Binds mutator.

  6. 13 Koen Williame 2008-07-07 02:33:11

    @kassens: I feel stupid to have suggested that... would you build another mutator for bindWithEvent? Or is there another way?

  7. 16 kassens 2008-07-09 12:33:30

    @Koen: there's no difference between bind and bindWithEvent when you don't pass a bind attribute. If you need to pass a special value, I don't think a mutator makes sense there.

  8. 101 Daniel Steigerwald 2008-08-24 02:05:59

    Great! Now I have my lovely instance scoped privates :-)

    Class.Mutators.Privates = function(self, staticPrivates) {
    var instancePrivates = {};
    if ($type(staticPrivates) == 'object') $merge(instancePrivates, staticPrivates);
    self.setPrivate = function(key, value) {
    privates.key = value;
    }
    self.getPrivate = function(key) {
    return privates.key;
    }
    };

  9. 102 Daniel Steigerwald 2008-08-24 02:18:54

    And all fun ends when I want to extend such class :(

    Prototyped methods has no other scope than this, and own closure, and this is ugly: MyClass.setPrivate(this, 'el', someEl);

  10. 285 Nathan White 2008-09-16 23:09:39

    @Daniel : I have been playing with this idea too. I also would like to create singleton classes. However the way Mutators work is it modifies the Object after initialization, thus you lose the prototype.

    Maybe I am being naive here but ideally Mutators should allow you to define a 'before', 'after'. All example of Mutators thus far are 'after' types. If we had a 'before' where we could tweak the prototype before initialization we might be able to pull some of these things off.

    Am I off base with that assertion Jan?

Leave a Reply



About

I'm a MooTools core developer and this is the place where I publish some snippets and other mootools related stuff. I study computer science in Darmstadt, Germany.

Idea for a Topic?

If you've got an idea for a topic to write about, let me know!

Other Blogs

User