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:
var MyClass = new Class({ initialize: function(){ this.bound = { sayHello: this.sayHello.bind(this), sayGoodbye: this.sayGoodbye.bind(this) } // now only using this.bound.sayHello and // this.bound.sayGoodbye inside the class }, sayHello: function() { /* ... */ }, sayGoodbye: function() { /* ... */ } });
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:
Class.Mutators.Binds = function(self, methods) { $splat(methods).each(function(method){ var fn = self[method]; self[method] = function(){ return fn.apply(self, arguments); }; }); };
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:
var MyClass = new Class({ Binds: ['sayHello', 'sayGoodbye'], initialize: function(){ // now only using this.sayHello and // this.sayGoodbye inside the class }, sayHello: function() { /* ... */ }, sayGoodbye: function() { /* ... */ } });
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.
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!
Ok. That's pretty slick.
I'll take 1 please, with fries.
... 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 ...
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)
@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.
@kassens: I feel stupid to have suggested that... would you build another mutator for bindWithEvent? Or is there another way?
@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.
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;
}
};
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);
@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?