Section 9.4. Common Object Methods


9.4. Common Object Methods

When defining a new JavaScript class, there are several methods that you should always consider defining. These methods are detailed in the subsections that follow.

9.4.1. The toString( ) Method

The idea behind toString( ) is that each class of objects has its own particular string representation, so it should define an appropriate toString( ) method to convert objects to that string form. When you define a class, you should define a toString( ) method for it so that instances of the class can be converted to meaningful strings. The string should contain information about the object being converted because this is useful for debugging purposes. If the string representation is chosen carefully, it can also be useful in programs themselves. Additionally, you might consider adding a static parse( ) method to your class to parse a string output by toString( ) back into object form.

The Complex class of Example 9-2 includes a toString( ) method, and the following code shows a toString( ) method you can define for a Circle class:

 Circle.prototype.toString = function ( ) {     return "[Circle of radius " + this.r + ", centered at ("         + this.x + ", " + this.y + ").]"; } 

With this toString( ) method defined, a typical Circle object might be converted to the string "[Circle of radius 1, centered at (0, 0).]".

9.4.2. The valueOf( ) Method

The valueOf( ) method is much like the toString( ) method, but it is called when JavaScript needs to convert an object to some primitive type other than a stringtypically, a number. Where possible, the function should return a primitive value that somehow represents the value of the object referred to by the this keyword.

By definition, objects are not primitive values, so most objects do not have a primitive equivalent. Thus, the default valueOf( ) method defined by the Object class performs no conversion and simply returns the object on which it is invoked. Classes such as Number and Boolean have obvious primitive equivalents, so they override the valueOf( ) method to return appropriate primitive values. This is why Number and Boolean objects can behave so much like their equivalent primitive values.

Occasionally, you may define a class that has some reasonable primitive equivalent. In this case, you may want to define a custom valueOf( ) method for the class. In the Complex class of Example 9-2, you'll see that a valueOf( ) method was defined that returned the real part of the complex number. Thus, when a Complex object is used in a numeric context, it behaves as if it were a real number without its imaginary component. For example, consider the following code:

 var a = new Complex(5,4); var b = new Complex(2,1); var c = Complex.sum(a,b);  // c is the complex number {7,5} var d = a + b;             // d is the number 7 

One note of caution about defining a valueOf( ) method: the valueOf( ) method can, in some circumstances, take priority over the toString( ) method when converting an object to a string. Thus, when you define a valueOf( ) method for a class, you may need to be more explicit about calling the toString( ) method when you want to force an object of that class to be converted to a string. To continue with the Complex example:

 alert("c = " + c);              // Uses valueOf( ); displays "c = 7" alert("c = " + c.toString( ));   // Displays "c = {7,5}" 

9.4.3. Comparison Methods

JavaScript equality operators compare objects by reference, not by value. That is, given two object references, they look to see if both references are to the same object. They do not check to see if two different objects have the same property names and values. It is often useful to be able to compare two objects for equality or even for relative order (as the < and > operators do). If you define a class and want to be able to compare instances of that class, you should define appropriate methods to perform those comparisons.

The Java programming language uses methods for object comparison, and adopting the Java conventions is a common and useful thing to do in JavaScript. To enable instances of your class to be tested for equality, define an instance method named equals( ). It should take a single argument and return true if that argument is equal to the object it is invoked on. Of course it is up to you to decide what "equal" means in the context of your own class. Typically, you simply compare the instance properties of the two objects to ensure that they have the same values. The Complex class in Example 9-2 has an equals( ) method of this sort.

It is sometimes useful to compare objects according to some ordering. That is, for some classes, it is possible to say that one instance is "less than" or "greater than" another instance. You might order Complex numbers based on their magnitude( ), for example. On the other hand, it is not clear that there is a meaningful ordering of Circle objects: do you compare them based on radius, X coordinate and Y coordinate, or some combination of these?

If you try to use objects with JavaScript's relation operators such as < and <=, JavaScript first calls the valueOf( ) method of the objects and, if this method returns a primitive value, compares those values. Since our Complex class has a valueOf( ) method that returns the real part of a complex number, instances of the Complex class can be compared as if they were real numbers with no imaginary part. This may or may not be what you actually want. To compare objects according to an explicitly defined ordering of your own choosing, you can (again, following Java convention) define a method named compareTo( ).

The compareTo( ) method should accept a single argument and compare it to the object on which the method is invoked. If the this object is less than the argument object, compareTo( ) should return a value less that zero. If the this object is greater than the argument object, the method should return a value greater than zero. And if the two objects are equal, the method should return zero. These conventions about the return value are important, and they allow you to substitute the following expressions for relational and equality operators:

Replace this

With this

a < b

a.compareTo(b) < 0

a <= b

a.compareTo(b) <= 0

a > b

a.compareTo(b) > 0

a >= b

a.compareTo(b) >= 0

a == b

a.compareTo(b) == 0

a != b

a.compareTo(b) != 0


Here is a compareTo( ) method for the Complex class in Example 9-2 that compares complex numbers by magnitude:

 Complex.prototype.compareTo = function(that) {     // If we aren't given an argument, or are passed a value that     // does not have a magnitude( ) method, throw an exception     // An alternative would be to return -1 or 1 in this case to say     // that all Complex objects are always less than or greater than     // any other values.     if (!that || !that.magnitude || typeof that.magnitude != "function")         throw new Error("bad argument to Complex.compareTo( )");     // This subtraction trick returns a value less than, equal to, or     // greater than zero.  It is useful in many compareTo( ) methods.     return this.magnitude( ) - that.magnitude( ); } 

One reason to compare instances of a class is so that arrays of those instances can be sorted into some order. The Array.sort( ) method accepts as an optional argument a comparison function that uses the same return-value conventions as the compareTo( ) method. Given the compareTo( ) method shown, it is easy to sort an array of Complex objects with code like this:

 complexNumbers.sort(new function(a,b) { return a.compareTo(b); }); 

Sorting is important enough that you should consider adding a static compare( ) method to any class for which you define a compareTo( ) instance method. One can easily be defined in terms of the other. For example:

 Complex.compare = function(a,b) { return a.compareTo(b); }; 

With a method like this defined, sorting becomes simpler:

 complexNumbers.sort(Complex.compare); 

Notice that the compareTo( ) and compare( ) methods shown here were not included in the original Complex class of Example 9-2. That is because they are not consistent with the equals( ) method that was defined in that example. The equals( ) method says that two Complex objects are equal only if both their real and imaginary parts are the same. But the compareTo( ) method returns zero for any two complex numbers that have the same magnitude. Both the numbers 1+0i and 0+1i have the same magnitude, and these two values are equal according to compareTo( ) but not according to equals( ). If you write equals( ) and compareTo( ) methods for the same Class, it is a good idea to make them consistent. Inconsistent notions of equality can be a pernicious source of bugs. Here is a compareTo( ) method that defines an ordering consistent with the existing equals( ) method:

 // Compare complex numbers first by their real part.  If their real // parts are equal, compare them by complex part Complex.prototype.compareTo = function(that) {     var result = this.x - that.x;   // compare real using subtraction     if (result == 0)                // if they are equal...         result = this.y - that.y;   //   then compare imaginary parts     // Now our result is 0 if and only if this.equals(that)     return result; }; 




JavaScript. The Definitive Guide
JavaScript: The Definitive Guide
ISBN: 0596101996
EAN: 2147483647
Year: 2004
Pages: 767

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net