I'm going to write a couple of blog posts, about a tiny code library I've written, which I think quite nicely shows the beauty and flexibility of the javascript language, as well as being a very useful implementation of
javabeans-like functionality in javascript.
Why would you want javabeans in javascript? Well for me, I needed a cross-platform way of being notified whenever an objects property is modified, previously I had classes with lots of methods that look like
this.setSize = function(s){
size = s;
signal(this, "actionChanged", "size", s);
};
The signal function is part of
MochiKit 1.3's new
Signal/Slot mechanism (very useful, I've been using it from cvs for quite a while now and don't know how i survived without it).
The first step in this process was to develop a method that generated the getters and setters for me.
function beanify(obj, props){
var properties = {};
for(var i=0; i<props.length; i++){
var propName = props[i].toString();
var getter = function(){
return properties[arguments.callee.propertyName];
};
getter.propertyName = props[i].toString();
var setter = function(o){
properties[arguments.callee.propertyName] = o;
};
setter.propertyName = props[i].toString();
obj["get"+props[i].charAt(0).toUpperCase()+props[i].substring(1)] = getter;
obj["set"+props[i].charAt(0).toUpperCase()+props[i].substring(1)] = setter;
}
}
It is a very simple bit of code, but quite elegant, it illustrates three very important concepts of javascript.
1.
Classes and objects are open - This means that new methods can be added to objects and classess (prototypes) on the fly.
2.
Closures can be used to create private variables - I want the properties array to be private, i.e. it can't be accessed from outside the generated methods. In javascript this is simple, I create a local variable in the outer function, this can be accessed by the inner functions (getter and setter) but not from anywhere else.
3.
Functions are objects - The getter and setter functions also need to know the name of the variable they are getting and setting, this must be stored in a variable somewhere. I can't store it in the beanify function ie.
var getter = function(){
return properties[propName];
};
because the propName variable is passed by reference not value, so propName is modified every time the loop iterates - thus when the getter or setter is actually called, propName = props[props.length-1]. The solution is to attach a copy of the property name to the getter/setter function object. This solution isn't quite perfect as the property name is publicly accessible, but I'm sure this can be fixed easily.
Thats all for now, but in Part 2 I will show the full glory of the JSBeans class, including how I use it to create the property changed events I need, and how it can be used as a cross-platform replacement for the netscape/mozilla watch() function.