Bill's Computer Circus
Don't get caught with your system down.
NOTICE: This web site may not render correctly in older browers like Internet Explorer 5.2 for the Mac. May the gods help you if you are using Internet Explorer on any machine! Otherwise, if this site does not look right on your browser, please let me know what browser you are using (and what version and on what computer). Thanks!
"Visual Basic makes the easy things easier. Delphi makes the hard things easy."
-- unknown
Saturday, December 17, 2005
 
JavaScript Objects
So, I have been using JavaScript off and on for years now, never really digging very deeply into it until recently. I am now playing with the object oriented aspects of the language.

Objects in JavaScript differ fundamentally in certain ways from other object-oriented languages (like Delphi, for instance), and I just learned some peculiar aspects about them today.

In JavaScript, everything that is not a primitive data type (such as a number), is an object. Even functions are objects. And, since JavaScript is interpreted and not compiled, it does things a little differently that allow you to define objects and object properties on the fly.

There is no explicit "Constructor" keyword to identify an object's constructor. An object's constructor is a function, just like any other function, and could be indistinguishable from any other function if you didn't know what you were looking at. But the name of the function is the name of the object "type", if you will, that you want to create. The actual object creation is performed by the "new" keyword.

So, if you had a constructor like the following:



function Robot( dangerous ) {
this.eatsPeople = dangerous || false;
}



Then, you can create an instance of this object by doing something like this:


var killerRobot = new Robot( true );


Now, there are a few things to explain here. The first has nothing to do with objects. If you notice in the Robot() function, the assignment uses a strange and seemingly pointless Boolean expression: this.eatsPeople = dangerous || false; Since "dangerous" is a boolean data type (well, not explicitly, but that's how it is being used), then what is the point of "dangerous || false"?

The thing about JavaScript is that you can call the Robot() function without any parameters, and it will still invoke the Robot() function. So, if I were to say


var friendlyRobot = new Robot();


It would invoke the Robot() function, but the "dangerous" parameter would resolve to the value of "undefined". And, in a conditional statement or expression, dangerous does not resolve to "true" or "false" - its value is always "undefined", which is not really valid in a Boolean value, so it must be translated somehow. When JavaScript evaluates a logical "OR" expression, it will continue to evaluate each term of the expression from left to right until one term succeeds, or the last term is evaluated, whichever comes first. Boolean "OR" evaluation is short-circuited when the first true condition is encountered, and the result of the expression is the result of the last term that was evaluated. Therefore, "dangerous || false", when evaluated, first looks at "dangerous". Its value is not "true", so the term fails and evaluation progresses to the next term of the expression. The next term is simply "false", which resolves, literally, to itself ("false"). The term fails, but there are no more terms to evaluate, so evaluation ends there and the final result is "false". Therefore, the property "eatsPeople" is assigned the value of "false".

If the expression was:


this.eatsPeople = dangerous || true;


then this.eatsPeople would be assigned the value of "true" if dangerous was "undefined". The only problem with doing this, however, is that this.eatsPeople will be set to true regardless of the value of dangerous.

So, anyway, enough of the sidebar. Notice the reference to "this" in the Robot() function. "This" refers to the current object in question. Since the new keyword results in the creation of an object, this is a signal to JavaScript to create an object, and - in this case - call the Robot() function to act as the constructor. The "this" reference in the Robot() function then refers to the new object just created. And the assignment to "this.eatsPeople" is actually defining a property for the instance of the Robot() object, even though there is no "var" keyword attached. This is how you can define new properties for objects on the fly in JavaScript. In this case, the property is created within the constructor.

In the main code, I could further add another property to my robot's instance if I wanted to, like this:


var killerRobot = new Robot(true);
killerRobot.reproduces = true;


This adds a new property "reproduces" to the killerRobot instance. Now, the important thing to remember here is that the reproduces property only exists on the killerRobot instance. If I created another instance of Robot, it would not have the reproduces property. In order to have all robots of type Robot, the property needs to be added to the Robot() constructor, so now the constructor might look something like this:



function Robot( dangerous ) {
this.eatsPeople = dangerous || false;
this.reproduces = false;
}



This would make all Robot instances friendly and alone by default. An additional parameter could be added as well to specify both properties in the constructor, but we won't do that here.

Now, one not-so-apparent thing to note here is that these properties are what is known as "instance" properties. In other words, they are properties that belong to the instance of the object and not to the object "class", itself. I hesitate to use the word "class" in the context of JavaScript objects, because JavaScript does not particularly include such a notion. But, because there is no formal "class" declaration available, there is no formal equivalent to a class property or a class method.

However, there is something comparable. For each object type in JavaScript, there is a "prototype". By assigning things to the prototype property, you can define properties and methods that are shared among all objects of the same time.

This has always been my concept of object-oriented constructs all along - that all instances of a class share the same exact code. But in JavaScript, if I were to add a method to the Robot() object in the following manner:



function Robot( dangerous ) {
this.eatsPeople = dangerous || false;
this.reproduces = false;
this.attack = function( target ) {
alert( "I'm going to kill "+target+"!" );
}
}



then I created two robots:


var killerRobot = new Robot(true);
var friendlyRobot = new Robot();


then each robot would have its own copy of an attack() function (even though friendlyRobot will probably never use it). This is contrary to other languages, such as Delphi, where you have a class that defines the methods (such as attack), and only one instance of all those methods exists and are available to all instances of that class, whether used or not.

The way to achieve this in JavaScript is to use the prototype property (though I'm not really sure it is a property). For example:



Robot.prototype.attack = function (target) {
alert( "I'm going to kill "+target+"!" );
};

function Robot( dangerous ) {
this.eatsPeople = dangerous || false;
this.reproduces = false;
}



Here, the attack method has been moved out of the constructor and up into the Robot object's prototype. This way, the function (method) is available to all instances of the Robot() object.


var killerRobot = new Robot(true);
var friendlyRobot = new Robot();
killerRobot.attack("friendlyRobot");
friendlyRobot.attack("some time");


The above code would produce the output:


I'm going to kill friendlyRobot!
I'm going to kill some time!


and with only a single instance of the attack() function in memory.

But now here is an odd thing about JavaScript objects. All the properties defined within the constructor exist only on a per-instance basis. In other words, I have to create an instance of a Robot in order to have access to properties named eatsPeople and reproduces. I cannot reference the object "class", itself, for such values. This is expected, since each instance is likely to have different properties (that's why properties exist, eh?).

What if these properties were moved into the Robot's prototype?



Robot.prototype.eatsPeople = false;
Robot.prototype.reproduces = false;
Robot.prototype.attack = function (target) {
alert( "I'm going to kill "+target+"!" );
};

function Robot( dangerous ) {
this.eatsPeople = dangerous || false;
}



Well, then, as you can see, we no longer need to assign this.reproduces = false in the constructor, since a definition already exists in the objects prototype. All Robot() objects will "inherit" a value of "false" for reproduces.

The same is true for the eatsPeople property. However, there is something that may not be so apparent happening here. If a robot is created, such as var killerRobot = Robot(true); it "inherits" the eatsPeople property value from the prototype...but the property does not actually exist as part of the instance of the robot. However, the Robot() constructor assigns a value to this.eatsPeople, which actually creates an instance property of that name for that particular instance. The value on the prototype is still there - the instance just can't see it any more.

This can demonstrated. I got to thinking about it, and came up with the following code snippit, tested it, and it produced exactly the results expected. Actually, let's demonstrate this using the Robot object to avoid veering down another path.

Study the following:



Robot.prototype.eatsPeople = false;
Robot.prototype.reproduces = false;
Robot.prototype.attack = function (target) {
alert( "I'm going to kill "+target+"!" );
};

function Robot( dangerous ) {
this.eatsPeople = dangerous || false;
}

// -- main code --
var killerRobot = new Robot(true);
alert( killerRobot.eatsPeople ); // Displays "true"
delete killerRobot.eatsPeople; // Remove instance value
alert( killerRobot.eatsPeople ); // Displays "false" - NOT "undefined"!



In the main code, an instance of Robot is created, called killerRobot, with a value of "true" in the parameter to set the eatsPeople property to "true". Displaying the value of eatsPeople verifies this is the case. Now, the next line deletes the eatsPeople property. You might think that displaying the value now would display an "undefined" value. But this is not the case. In fact, the value "false" is displayed. This is because the instance property eatsPeople has been removed, but the prototype's version is still there...and the reference to killerRobot.eatsPeople resolves to the prototype's value.

So this seems to beg a modification to the Robot() constructor. Since the prototype's default value is "false", there is really no reason to clutter up memory by creating another instance of eatsPeople with the same value, so why bother creating it if the value of dangerous is false or undefined? It seems we only want to override the default value if the new value is "true". Therefore, to save memory, we can do the following:



function Robot( dangerous ) {
if (dangerous) this.eatsPeople = true;
}



Got that?

OK, now if I really wanted to spend the time to dig deeper here, I would get into the aspect of how functions are actually objects, too, and how you can define properties and methods (i.e. other functions) on functions. For example, Robot.speedLimit = 35; or Robot.reproduce = function (dangerous) { ... };. This sets up the equivalent of "static" or "class" properties and methods, meaning thay can be referenced and invoked without the need to work from an instance of the object.

But I won't get into that now. Go rest, now. You deserve it.

posted by Bill  # 4:49 PM