thomasfrank.se
Classy JSON
June 9, 2006
Update August 31, 2006: A new/improved way of doing things
Classier JSON -- if you are new to the subject I suggest that you read this one instead.
The current trend among many JavaScript developers seems to be to write your code JSON style, i.e. to collect a number of functions/methods into a singleton object written in object notation.
If you need an introduction to this, I suggest reading JSON for the masses by Dustin Diaz.
I for one agree that this solves many problems:
- You don't clutter the global namespace.
- Your code gets more object-oriented and is easier to reuse.
- JSON notation tends to be easier to read than tradional constructors or plain old heaps of functions.
What about JSON and classes?
JSON objects are singleton objects by design, not by choice, since they have no constructor. I really like JSON, but I like classes too. Therefore I have written a small (5 kB) freeware library called classyJSON. Download it and use it to:
- Write classes/object constructors JSON style.
- Automate inheritance (and multiple inheritance).
- Add private members to your classes.
- Make deep copies of objects.
- Join several objects into one.
To use classyJSON you simply embed it in the head section of your web pages, before any other scripts that uses it.
<head>
<script type="text/javascript" src="classyJSON.js"></script>
...
</head>
Classify your objects
Let us start by writing our first class in JSON -- Vehicle. As you may notice, everything looks just like the creation of a normal JSON object up until .classify(), an object method which converts any object into a class:
// The Vehicle class
Vehicle={
wheels:2,
setWheels:function(x){
this.wheels=x
},
getWheels:function(x){
return this.wheels
}
}.classify();
// Let's try to make some instances of Vehicle
myBike=new Vehicle();
myTractor=new Vehicle();
myTractor.setWheels(4);
// This should return "2 and 4"
alert(myBike.getWheels()+" and "+myTractor.getWheels());
Inheritance
To make a class inherit another class we use the constructor-method inherite():
// Be sure to include our previous definition of Vehicle here...
// The Car class (which inherits Vehicle)
Car={
wheels:4
}.classify().inherit(Vehicle);
// This should return "4"
myCar=new Car();
alert(myCar.getWheels());
Multiple inheritance
You might have a class called Vehicle and another one called Investment and then decide that you want the Car class to inherit them both. You would write this as: Car={wheels:4}.classify().inherit(Vehicle, Investment).
Please note:
- In what order you let your class inherit other classes may mattter -- in this example class members from Investment overwrites those from Vehicle if they have the same name.
Private members
A while ago, Douglas Crockford pointed out that you can get private members in JavaScript objects -- properties (and methods) that only the methods of the object itself can reach.
I thought it would be nice if we could use those with our JSON style classes as well, so I wrote a few constructor-methods to make this possible:
- privateMembers()
- publicMembers()
- privateMemberTypes()
- publicMemberTypes()
Each of these methods takes one argument -- a comma-separated string that lists which members (or member data types) that should be made private or public.
An important thing to remember here is that each call to one of these functions resets the private/public state of the class members -- you can't combine the methods to set up a certain state. Let us look at an example:
// The Car class
Car={
wheels:4,
setWheels:function(x){
this.wheels=x
},
getWheels:function(x){
return this.wheels
}
}.classify();
/* Some test instances
(The "..Types"-methods will recognize the following data types:
object,array,function,string,number,boolean) */
Car=Car.privateMembers("wheels,setWheels")
car1=new Car();
Car=Car.publicMembers("wheels");
car2=new Car();
Car=Car.privateMemberTypes("number");
car3=new Car();
Car=Car.publicMemberTypes("function")
car4=new Car();
// Let's check which public members each instance have
var x=""; var op=Object.prototype;
var inspect=function(x){var y=[]; for(var i in x){if(!op[i]){y.push(i)}};return y};
for(var i=1;i<=4;i++){x+="car"+1+"\t"+eval("inspect(car"+i+").join(', ')")+"\n"};
alert(x);
/* If everything works correct the alert should read like this:
car1 getWheels
car2 wheels
car3 getWheels, setWheels
car4 getWheels, setWheels */
Please note:- You can't use bracket syntax along with this for private members, i.e. this will not work: function getWheels(){return this["wheels"]}.
What about inheritance?
The good news is that the methods inherite(),deepCopy() and objJoin() will work with private members created with our private/public methods.
Copy objects
There is no built-in method in JavaScript that will copy objects -- assigning the value of an variable to an object variable will just create another pointer to that object. Sometimes pointers are what you want, sometimes you really want a new unique object -- so I added the object method deepCopy():
// The alert below will return "3 and 3" since "a" and "b"
// are just different pointers to the same object
var a={wheels:4};
var b=a;
a.wheels=3;
alert(a.wheels+" and "+b.wheels);
// The alert below will return "4 and 3" since "a" and "b"
// are pointers to two different unique objects
var a={wheels:4};
var b=a.deepCopy();
a.wheels=3;
alert(a.wheels+" and "+b.wheels);
Please note:
- This method will work with arrays as well as objects.
- Beware of trying to copy cyclical objects or other objects in which you deliberately use pointers as members (if you want them to stay just pointers).
Join objects
To be able to join two or more objects into one often comes in handy. So, lo and behold, the object method objJoin():
// Three objects
var car={wheels:4};
var favlooks={color:"blue",look:"expensive"};
var priceRange={price:"cheap"};
// Let us join them in to one - myCar
myCar=car.objJoin(favlooks,priceRange);
// Now, let's check the members of myCar
var myCarMembers=function(){var x="";for(var i in myCar){
if(!Object.prototype[i]){x+=i+": "+myCar[i]+"\n"}};return x};
alert(myCarMembers());
// Of course objJoin() is an easy way to add members on the fly too
myCar=myCar.objJoin({maxSpeed:150,height:5,automatic:true});
// Let's check the members of myCar again
alert(myCarMembers());
Conclusion
That's about all. I hope classyJSON will provide you with a small framework that lets you use JSON for new classy things. Enjoy -- and feel free to bug report and comment below.