thomasfrank.se

Classier JSON

August 31, 2006

A while ago I wrote an article called Classy JSON. The basic idea presented was that you should be able to create JavaScript classes from objects definied JSON style (as shown by Dustin Diaz in JSON for the masses).
In that article I also presented a script that creates classes from objects and handles inheritance. Some, like Ian Selby, liked my efforts, but there were also many who complained. Mostly about that I modified the Object.prototype.
To be fair I think you could make worse complaints about my old code if you study it in detail - it isn't very memory efficient and also does some rather hacky eval tricks to switch object members from private to public and back again. Nice functionality, but a murky implementation beneath the surface.
So no more of that - I'm back with a more basic, memory efficient and ten times as small script, with a nice prototypal inheritance pattern. And I steer clear of the Object.prototype.

The script

Here's my new script:
// jsonClass - a constructor of constructors 
// is added as a method to the Array.prototype
Array.prototype.jsonClass=function(){

	// Remember the array as x in closures/inner functions
	var x=this;

	// Create our object constructor 
	var constr=function(){
		var a=arguments; var u;
		for(var i=0;i<x.length;i++){
			if(typeof x[i]=="object"){
				for(var j in x[i]){this[j]=this[j]!=u?this[j]:x[i][j]}
			};
			if(typeof x[i]!="string" || a[i]===u){continue};
			this[x[i]]=a[i];
		}
	};

	// Prototype based inheritance of all superclasses
	var nextProto=false;
	for(var i=0;i<=x.length;i++){
		var a=x[i]||constr;
		if(typeof a=="function"){
			a.prototype=nextProto||a.prototype;
			nextProto=new a();
		}
	};

	// Add a method to the constructor that lets us add
	// members to a class (and subclasses) after it has 
	// been created
	constr.addMembers=function(obj){
		for(var i in obj){constr.prototype[i]=obj[i]}
	};

	// Return a our object constructor
	return constr
};

You can also download it (0.5 kB without comments).

So how do we use that?

Well basically the script enables us to create classes/constructors from arrays. For such an array the following holds true:

Class creation

Person=[
	'name','height',
	{
		name:'John Doe (default name)',
		height:'6 feet (default height)',
		getName:function(){return this.name},
		getHeight:function(){return this.height}
	}
].jsonClass();

Inheritance

Here we create a class that inherits from another class:
Student=[
	'name','height','grade',
	Person,
	{
		grade:'C (default grade)',
		getGrade:function(){return this.grade}
	}
].jsonClass()

Instance creation

And here we create a new object - an instance of Student:
var Bob=new Student('Bob','5 ft 11 in','A');

Adding members to a class

A nice thing about our prototypal inheritance is that we can add members to a class after it has been created.
// At the moment this would return undefined
alert(Bob.cool)

// Now we add cool to Bob's superclass
Person.addMembers({cool:'supercool'})

// And now this will return 'supercool'
alert(Bob.cool)

Conclusion

Thanks to Peter Foti for getting me to think a second time about classes and JSON.
I think this syntax is clean and efficient, but then again I like json-esque coding style a lot. Some don't. Let me know what you think!
[comments]