Augmenting JavaScript objects with toString and valueOf

Today I want to present two useful methods: toString and valueOf. Both of these methods are used by JavaScript interpreter when converting objects to primitive types.

We will start with toString that can be useful for debugging purposes, say we have an object:

var point = {
  x: 10,
  y: 20
};

When we try to convert it to String e.g. using '' + point expression, we get (in Chrome):

[object Object]

Wouldn’t it be nice to get (10, 20)? With support of toString we can do it, simply let’s augment our point with toString method:

point.toString = function() {
    return '(' + this.x + ', ' + this.y + ')';
};

now String(point) returns:

"(10, 20)"

This works too when we concatenate our point with string, or when we are join’ing array of points:

> 'current position: ' + point
"current position: (10, 20)"

> [point, point, point].join('; ');
"(10, 20); (10, 20); (10, 20)"

It will also work in any other situation when object is coerced to String type. Unfortunately it doesn’t work with console.log:

> console.log(point)
Object {x: 10, y: 20}

Now we go into interesting topic: when JavaScript objects are converted to string’s? We already given two examples: when object is concatenated with string and when we explicitly convert object to string via String(obj) call. But this will also happen when we use object with operators like > or >=. Exact rules are pretty compiled and if your are interested in them I advice reading chapter 8 (Type coercion) and 9 (Operators) from excellent Speaking JS book. For now let’s consider simple example, what will happen when we try to use > on points with toString method:

var Point = function(x, y) {
    this.x = x;
    this.y = y;
};

Point.prototype.toString = function() {
    return '(' + this.x + ', ' + this.y + ')';
};

var p1 = new Point(10, 20);
var p2 = new Point(20, 30);
var p3 = new Point(20, 15);

When interpreter executes expression like p1 > p2 first it tries to convert objects to primitives - first by calling valueOf method (by default it will return this) and if it not return primitive value then it tries toString. Since we are providing our own version of toString that returns primitive value (a String) interpreter will use values returned by toString to compare points, so:

> // because '(20, 30)' > '(10, 20)'  - strings in JS are compared lexicographically
> p1 > p2
false
> p2 > p1
true
> // because '(20, 30)' > '(20, 15)':
> p2 > p3
true

Looks like we overloaded > operator in JavaScript, yay! But we must be aware of limitations of this technique: first we are comparing strings not object properties, second string in JS are compared lexicographically so '2' > '111'. In other words don’t use it in production code it may cause too much confusion, explicit method like Point.compare would be much better.

Now we can turn to valueOf method, in it’s working it is similar to toString method, only difference is that it is called when object must be converted to Number. Let’s see quick example:

var obj = {
    valueOf: function() {
        return 42;
    }
};

console.log(Number(obj)); // prints 42

Object is converted to number when used with operators like: +, * and -. Also valueOf is used when objects are compared using > or >= operators. valueOf is not as useful as toString IMHO, but it’s worth to know. One more fact is that Date objects have custom implementation of valueOf method that returns number of milliseconds from epoch (in other words it returns same value as getTime()). Thanks to this we can use > to compare dates, and get difference in milliseconds between dates as: date2 - date1.