Article
Rethinking JavaScript Objects
With this code in place, we can now do the following:
function ClassB() {
this.addProperty(“message”, “Hello world”);
this.addProperty(“name”, “Nicholas C. Zakas”);
}
ClassB.prototype.onpropertychange = function(oEvent) {
if (oEvent.propertyName == “name”) {
oEvent.returnValue = false; //don’t allow name to be changed
}
}
var Test = new ClassB();
alert(Test.getMessage()); //outputs “Hello world”
Test.setMessage(“Goodbye world”);
alert(Test.getMessage()); //outputs “Goodbye world”
alert(Test.getName()); //outputs “Nicholas C. Zakas”
Test.setName(“Michael A. Smith”);
alert(Test.getName()); //outputs “Nicholas C. Zakas”
A slight addition can be made in order to allow detection of the type of value being stored in a property. In effect, we are adding type checking to any properties added with the addProperty() method:
Object.prototype.addProperty = function (sType, sName, vValue) {
if (typeof vValue != sType) {
alert(“Property “ + sName + “ must be of type “ + sType + “.”);
return;
}
this[sName] = vValue;
var sFuncName = sName.charAt(0).toUpperCase() + sName.substring(1, sName.length);
this["get" + sFuncName] = function () { return this[sName] };
this["set" + sFuncName] = function (vNewValue) {
if (typeof vNewValue != sType) {
alert(“Property “ + sName + “ must be of type “ + sType + “.”);
return;
}
var vOldValue = this[“get” + sFuncName]();
var oEvent = {
propertyName: sName,
propertyOldValue: vOldValue,
propertyNewValue: vNewValue,
returnValue: true
};
this.onpropertychange(oEvent);
if (oEvent.returnValue) {
this[sName] = oEvent.propertyNewValue;
}
};
}
Here, I added a single parameter, sType, which defines the type of data the property holds. I made it the first parameter because, again, this is similar to Java. I also added two checks using the JavaScript typeof operator: one on the initial value assignment, another when the property is changed (in reality, these should throw errors, but for compatibility with Netscape 4.x, I opted for alerts). For those who are unaware, the typeof operator returns one of the following values:
- “undefined” – the value doesn’t exist.
- “string”
- “number”
- “function”
- “object”
- “boolean”
The parameter sType must match one of these values in order for this to be a valid check. In most cases, this should be good enough (if not, you can always write your own function to use instead of typeof). It’s important to note that a value of null will return “object” from the typeof operator.
Updating the previous example, we can now do this:
function ClassB() {
this.addProperty(“string”, “message”, “Hello world”);
this.addProperty(“string”, “name”, “Nicholas C. Zakas”);
this.addProperty(“number”, “age”, 25);
}
ClassB.prototype.onpropertychange = function(oEvent) {
if (oEvent.propertyName == “name”) {
oEvent.returnValue = false; //don’t allow name to be changed
}
}
var Test = new ClassB();
alert(Test.getMessage()); //outputs “Hello world”
Test.setMessage(“Goodbye world”);
alert(Test.getMessage()); //outputs “Goodbye world”
alert(Test.getName()); //outputs “Nicholas C. Zakas”
Test.setName(“Michael A. Smith”);
alert(Test.getName()); //outputs “Nicholas C. Zakas”
alert(Test.getAge()); //outputs 25
Test.setAge(“45”); //generates error message
alert(Test.getName()); //outputs 25
Conclusion
While JavaScript has left us with some severe limitations in the creation of custom objects and classes, it is also flexible enough to find solutions. JavaScript seems to be heading in a direction that brings it closer to Java in syntax and implementation (see the (JavaScript 2.0 proposal), but in the meantime the code presented in this article should make your development efforts a little less painful.
I have tested the code presented in this article on Netscape Navigator 4.79, Internet Explorer 6.0 and Netscape 7.0 (Mozilla 1.0.1), but I believe it should work in most modern browsers.