Tuesday, February 07, 2006

Some things I am doing with javascript right now.

Lately, I'm developing a bunch of (D)HTML Widgets. Basically, it's just fun, there's only a slightly serious undertone. Although I (as well as so many others) have developed lots and lots of this stuff already, I've set a few goals. Theyre modest, I gueass, but enough to make it interesting (to me, that is)


  • The associated javascript code should not easily clash with other javascripts obtained from other sources

  • Javascript and widgets should be reasonably thin and perform reasonable fast



Avoiding namespace clashes


Well, first one's easy when you take it into account in advance. The trick I'm using goes like this:


//create the root of my little framework
var org = new Object();

//fab is the project codename;
//you may think of it as FABricated
//or FABulous. You can also think of it as
//Fabulous Ajax Basis, or Framework for Ajax Builds.
org.fab = new Object();

//util will be the namespace for utility objects
org.fab.util = new Object();

//reflect will be the namespace for javascript metadata/reflection/introspection
org.fab.reflect = new Object();

//ui will be the namespace for
//user-interface related stuff, e.g. widgets
org.fab.ui = new Object();



Well, so far, nothing interesting, just a few objects doing nothing. Now, I'm hooking up my javascript functions and Constructors into these:


//some of the stuff in the org.fab.reflect namespace:

org.fab.reflect.isObject = function (
v
){
if (org.fab.reflect.isNull(v)){
return false;
}
return typeof(v)=="object";
}

org.fab.reflect.isArray = function (
obj
){
if (org.fab.reflect.isObject(obj)){
return org.fab.reflect.getFunctionName(
obj.constructor
)=="Array"
} else {
return false;
}
}

//Collection constructor
org.fab.util.Collection = function (
array
){
if (org.fab.reflect.isArray(array)){
this.items = array;
} else {
this.items = new Array();
}
this.currentItem = -1;
return this;
}


And now we have a nice, extensible system of categories to structure all javascript code.

(Actually, I just found out that ActiveWidgets use a similar technique to achieve this kind of categorization. LOL!)

Of course, this does not totally prevent our code from clashing with someone else's, but it will avoid most of the trouble most of the time. When you need to do a lot with just one particular branch of the framework, you can always use the javascript with scope modifier:


var v;
with (org.fab.reflect){
if (isObject(v)){
//yadda yadda yadda
} else if (isArray(v)) {
//yadda yadda yadda
}
}


Performance


I see a lot of object oriented javascript examples that do stuff like this:


function MyConstructor(){
//a normal property
this.property = null;

//a 'meant to be' static property
this.staticProperty = "Please do not change this";

return this;
}


The intention is to create a constructor for objects. The objects will have two properties: property and staticProperty. If course staticProperty was meant to be static, but it isnt. Javascript does not know true static objects or variables like you have in C/C++ or java.

However, this can be improved quite a lot by using the builtin prototype property of the constructor:


function MyConstructor(){
//a normal property
this.property = null;
return this;
}

//a static property
MyConstructor.prototype.staticProperty = "Please do not change this";


Apart from the fact that this resembles a static propery much better than the previous example, this implementation will also cost less memory. There is really only one instance of MyConstructor.prototype.staticProperty; in the previous case, the property would be created a new for each new call to the constructor.

The prototype is also very useful to adorn our Class (or actually, it's only a a constructor in javascrispeak) with methods:


MyConstructor.prototype.method = function(
arg
){
alert(arg + ": " + this.property);
}


This is not so different from the static property. Instead of assiging a function reference to this, we assign it to the prototype. The fun is that you can still invoke the method in an entirely natural way:


var m = new MyConstructor();
m.method("prompt: ");


So, even though we created the method as a member of the prototype property of MyConstructor, we can call it without using the full: MyConstructor.prototype.method("prompt: "). In fact, that would not've worked even if we wanted. That's because our method references this. When called as MyConstructor.prototype.method("prompt: ") the this 'pointer' would reference the MyConstructor.prototye; and presumably, this object does not have a property named property

If course, the MyConstructor.prototype.method() is useful when creating static functions. Then, this syntax is the only right and consistent one.

Surely, this is an efficient way of adorning constructors with methods, at least more so than:


function MyConstructor(){
//a normal property
this.property = null;

//a method
this.method = function(arg){
alert(arg + ": " + this.property);
}
return this;
}



In my opinion, it also beats this syntax:


function MyConstructor(){
//a normal property
this.property = null;

//a method
this.method = new Function(
'arg'
, 'alert(arg + ": " + this.property);'
);
return this;
}



next time: how to get Mozilla to render a HTML table anew after programmatically changing the HTML table's layout.

2 comments:

Anonymous said...

You should take a look at Ajile [ http://ajile.sf.net/ ]. Makes it easy to create and work with namespaces in JavaScript. Really good work.

rpbouman said...

Thanks for the pointer! I've been programming javascript for a couple of years, and although I think I'm not boasting when I say that I'm quite experienced in that field, other people keep amazing me with creative solutions like this. Marvellous!

DuckDB Bag of Tricks: Reading JSON, Data Type Detection, and Query Performance

DuckDB bag of tricks is the banner I use on this blog to post my tips and tricks about DuckDB . This post is about a particular challenge...