For any object in JavaScript, there are three special methods
that control the way the object is manipulated. Each of these
methods is automatically invoked by JavaScript to manipulate the
object in some way. By providing a custom definition of the
method, you can control the way an object is manipulated. The
methods are toString(), which is invoked to
convert the object to a string, valueOf(),
which is invoked to convert the object to a number or other
nonobject type, and assign(), which is
invoked to assign a value to the object. These three methods are
detailed in the sections below.

The toString()
method takes no arguments and returns a string, which should
somehow represent the type and/or value of the object referred
to by this. JavaScript invokes this method
whenever it needs to convert an object to a string. This
occurs, for example, when you use the +
operator to concatenate a string with an object, or when you
pass an object to a method like
document.write(). The default
toString() method for user-defined objects is
not very informative. For example, the following lines of code
simply cause the browser to display the string "[object Object]":

c = new Circle(1, 0, 0);
document.write(c);

You can define your own toString() method so
that your objects can be converted to more meaningful strings
that contain more information about the object being converted.
This is very useful when debugging programs, and if the string
conversions are chosen carefully, it can also be useful in the
programs themselves.

The toString() method is an excellent
candidate, of course, for inclusion in a prototype object when
defining a class of JavaScript objects. We might write and
register a toString() method for our
Circle class as follows:

The valueOf() method is much like the
toString() method, but is called when
JavaScript needs to convert an object to some type other than an
object or a string, typically a number. It takes no arguments,
and should return a number, Boolean, or function that somehow
represents the "value" of the object referred to by the
this keyword.

Most objects are more complicated than number or Boolean values,
and so the valueOf() method is not often
used. In fact, its main purpose is for use with the Number,
Boolean, and Function objects, for which it returns the
corresponding number, Boolean, or function value. For most
objects, the default valueOf() method simply
returns the object itself; this is a way of indicating that the
object could not be converted to any nonobject type. You may
occasionally find circumstances in which you can meaningfully
convert an object to a primitive type, and in these cases, you
may want to provide a custom definition of the
valueOf() method.

Suppose, for example, that you define a class of Complex objects
that represent complex numbers. This class will define methods
for arithmetic on complex numbers, but you'd still like to be
able to use your Complex objects with the regular arithmetic
operators, as if they were real numbers. You might do so with
code like that shown in Example 7.6.

function Complex(x,y) {
this.x = x; // real part of complex number
this.y = y; // imaginary part of complex number
}
// force the prototype object to be created
new Complex(0,0);
// define some methods
Complex.prototype.valueOf = new Function("return this.x");
Complex.prototype.toString = new Function("return '{'+this.x+','+this.y+'}'");
// create new complex number object
c = new Complex(4,1);
// Now rely on the valueOf() operator to treat it like a real number.
// Note that this wouldn't work with the + operator--that would convert
// the object to a string and do string concatenation.
x = c * 2; // x = 8
x = Math.sqrt(c); // x = 2

The assign() method is a new feature of
Navigator 3.0, and supports a kind of C++-style "operator
overloading" for the = operator.
The assign() method of an object is invoked
when that object appears on the left-hand side of an assignment
operator. It is passed one argument, which is the value on the
right-hand side of the operator. The purpose of the method is
in some fashion to assign the value passed as an argument to the
object referred to by the this keyword. The
default version of this method simply performs an assignment,
replacing the object on the left-hand side of the operator with
the new value from the right-hand side. You would define a
custom assign() method when you want the
assignment to behave differently.

One use of the assign() method is to
implement an assignment with side effects. Client-side
JavaScript does this with the Location object stored in the
Window.location property. When a string
containing a URL is assigned to this Location object, two things
happen. First, the URL is parsed, and its various components
are assigned to the properties of the Location object. And
second, and more importantly, the web browser reads the contents
of the new URL and displays them. This all occurs as the
side effect of an assignment, and is implemented with a custom
assign() method.

Another use of the assign() method is to make
objects read-only. If you define an assign method that does
nothing, then no one will be able to change the value of the
variable that holds your object. For example:

// give an object an empty assign() method
function no_op() { /* do nothing */ }
o = new Object();
o.assign = no_op;
// Now, no one can overwrite o. It will always contain the object we created.
o = 3; // has no effect
o = new Date(); // has no effect
// Note, though that we can assign properties to o:
o.x = 3; // this works fine

This technique can be extended to print issue a warning if
any attempt is made to overwrite the object. You might do it
with an assign() method defined like this:

Finally, the assign() method can be used to
change the very way that assignment is done. Objects are
usually assigned "by reference". That is, when one object is
assigned to another, the contents of the object are not copied;
instead, a reference to the new object merely overwrites a
reference to the old. (The concept of assignment "by reference"
is explained in detail in Chapter 9, Further Topics in JavaScript.)
If you want the contents of an object to be
copied when you assign one to another, you can do so with an
assign() method like the following:

function assign_properties(value)
{
// if the value is an object, copy it property by property
// otherwise, do nothing and leave the variable unchanged.
if (typeof value == "object")
for (prop in value) this[prop] = value[prop];
}
MyClass.prototype.assign = assign_properties;

The assign() method is one of the most
obscure and least elegant features of JavaScript. The
JavaScript developers at Microsoft did not support it in
Internet Explorer 3.0, and don't plan to support it in future
versions of the language either. Even the JavaScript designers
at Netscape aren't happy with assign(); they
are thinking about providing similar functionality through a
cleaner, more general mechanism in a future version of
JavaScript. For these reasons, the assign()
method may be one of the least portable features of JavaScript,
and you should think twice before writing code that relies upon
it.