AJAX In Action [289]
With inheritance comes the concept of scope. The scope of an object’s methods or properties determines who can use it—that is, whether it is public, private, or protected. Licensed to jonathan zheng Objects in JavaScript 601 Scope and inheritance can be useful features when defining a domain model. Unfortunately, JavaScript doesn’t support either natively. That hasn’t stopped people from trying, however, and some fairly elegant solutions have developed. Doug Crockford (see the Resources section at the end of this appendix) has developed some ingenious workarounds that enable both inheritance and scope in JavaScript objects. What he has accomplished is undoubtedly impressive and, unfortunately, too involved to merit a detailed treatment here. The syntax that his techniques require can be somewhat impenetrable to the casual reader, and in a team-based project, adopting such techniques should be considered similar to adopting a Java framework of the size and complexity of Struts or Tapestry—that is, either everybody uses it or nobody does. I urge anyone with an interest in this area to read the essays on Crockford’s website. Within the world of object orientation, there has been a gradual move away from complex use of inheritance and toward composition. With composition, common functionality is moved out into a helper class, which can be attached as a member of any class that needs it. In many scenarios, composition can provide similar benefits to inheritance, and JavaScript supports composition perfectly adequately. The next stop in our brief tour of JavaScript objects is to look at reflection. B.2.5 Reflecting on JavaScript objects In the normal course of writing code, the programmer has a clear understanding of how the objects he is dealing with are composed, that is, what their properties and methods do. In some cases, though, we need to be able to deal with completely unknown objects and discover the nature of their properties and methods before dealing with them. For example, if we are writing a logging or debugging system, we may be required to handle arbitrary objects dumped on us from the outside world. This discovery process is known as reflection, and it should be familiar to most Java and .NET programmers. If we want to find out whether a JavaScript object supports a certain property or method, we can simply test for it: if (MyObject.someProperty){ ... } This will fail, however, if MyObject.someProperty has been assigned the boolean value false, or a numerical 0, or the special value null. A more rigorous test would be to write if (typeof(MyObject.someProperty) != "undefined"){ Licensed to jonathan zheng 602 APPENDIX B JavaScript for object-oriented programmers If we are concerned about the type of the property, we can also use the instanceof operator. This recognizes a few basic built-in types: if (myObj instanceof Array){ ... }else if (myObj instanceof Object){ ... } as well as any class definitions that we define ourselves through constructors: if (myObj instanceof MyObject){ ... } If you do like using instanceof to test for custom classes, be aware of a couple of “gotchas.” First, JSON doesn’t support it—anything created with JSON is either a JavaScript Object or an Array. Second, built-in objects do support inheritance among themselves. Function and Array, for example, both inherit from Object, so the order of testing matters. If we write function testType(myObj){ if (myObj instanceof Array){ alert("it's an array"); }else if (myObj instanceof Object){ alert("it's an object"); } } testType([1,2,3,4]); and pass an Array through the code, we will be told—correctly—that we have an Array. If, on the other hand, we write function testType(myObj){ if (myObj instanceof Object){ alert("it's an