AJAX In Action [287]
Licensed to jonathan zheng Objects in JavaScript 597 B.2.2 Constructor functions, classes, and prototypes In OO programming, we generally create objects by stating the class from which we want them to be instantiated. Both Java and JavaScript support the new keyword, allowing us to create instances of a predefined kind of object. Here the similarity between the two ends. In Java, everything (bar a few primitives) is an object, ultimately descended from the java.lang.Object class. The Java virtual machine has a built-in understanding of classes, fields, and methods, and when we declare in Java MyObject myObj=new MyObject(arg1,arg2); we first declare the type of the variable and then instantiate it using the relevant constructor. The prerequisite for success is that the class MyObject has been declared and offers a suitable constructor. JavaScript, too, has a concept of objects and classes but no built-in concept of inheritance. In fact, every JavaScript object is really an instance of the same base class, a class that is capable of binding member fields and functions to itself at runtime. So, it is possible to assign arbitrary properties to an object on the fly: MyJavaScriptObject.completelyNewProperty="something"; This free-for-all can be organized into something more familiar to the poor OO developer by using a prototype, which defines properties and functions that will automatically be bound to an object when it is constructed using a particular function. It is possible to write object-based JavaScript without the use of prototypes, but they offer a degree of regularity and familiarity to OO developers that is highly desirable when coding complex rich-client applications. In JavaScript, then, we can write something that looks similar to the Java declaration var myObj=new MyObject(); but we do not define a class MyObject, but rather a function with the same name. Here is a simple constructor: function MyObject(name,size){ this.name=name; this.size=size; } We can subsequently invoke it as follows: var myObj=new MyObject("tiddles","7.5 meters"); alert("size of "+myObj.name+" is "+myObj.size); Licensed to jonathan zheng 598 APPENDIX B JavaScript for object-oriented programmers Anything set as a property of this in the constructor is subsequently available as a member of the object. We might want to internalize the call to alert() as well, so that tiddles can take responsibility for telling us how big it is. One common idiom is to declare the function inside the constructor: function MyObject(name,size){ this.name=name; this.size=size; this.tellSize=function(){ alert("size of "+this.name+" is "+this.size); } } var myObj=new Object("tiddles","7.5 meters"); myObj.tellSize(); This works, but is less than ideal in two respects. First, for every instance of MyObject that we create, we create a new function. As responsible Ajax programmers, memory leaks are never far from our minds (see chapter 7), and if we plan on creating many such objects, we should certainly avoid this idiom. Second, we have accidentally created a closure here—in this case a fairly harmless one—but as soon as we involve DOM nodes in our constructor, we can expect more serious problems. We’ll look at closures in more detail later in this appendix. For now, let’s look at the safer alternative, which is something known as a prototype. A prototype is a property of JavaScript objects, for which no real equivalent exists in OO languages. Functions and properties can be associated with a constructor’s prototype. The prototype and new keyword will then work together, and, when a function is invoked by new, all properties and methods of the prototype for the function are attached to the resulting object. That sounds a bit strange, but it’s simple enough in action: function MyObject(name,size){