JavaScript and valueOf

In JavaScript, valueOf and toString are sister methods inherited by every object. One of these methods will get invoked whenever an expression encounters a complex object where a primitive value was expected. For example :-

alert(myHamster);
var result = 2 + myHamster;

In broad terms, if the expression hints at the need for a string then toString is invoked,  otherwise its valueOf . If either method returns a non-primitive, the other method gets a try. The above examples expected myHamster to be a string and a number respectively so they are evaluated as:-

alert(myHamster.toString()); //interpreter expected a string
var result = 2 + myHamster.valueOf(); //expected a number


[See ECMA 5 chapter 8.12.8 for the full algorithm. Be aware that [[DefaultValue]], hint and ToPrimitive are internal constructs]

Based on these rules, valueOf is normally expected to return a meaningful non-string representation of the object.

We can take advantage of valueOf to make a shorthand syntax for the current date expressed in milliseconds from the epoch :-

(new Date()).valueOf(); //1272732879779 (date in ms)
+ new Date(); //1272732929260 (expected a non-string primitive after +)
+new Date; //1272732929399 (same thing but even shorter syntax)

This is useful if you frequently need to roll your own profiling metrics.

Most other default implementations  of valueOf are not very interesting :-

Boolean(true).valueOf(); //true
Number('123').valueOf(); //123
"aaa".valueOf(); //"aaa"

What’s more interesting (and you knew this was coming) is defining your own valueOf implementations :-

var toDollarRate = {
    pounds: 1.5,
    euros: 1.1
}

var Drink = function(name, cost, currency) {
    this.name = name;
    this.cost = cost;
    this.currency = currency;
}

Drink.prototype.costInDollars = function() {
    return this.cost * (toDollarRate[this.currency] || 1);
}

var boddingtons = new Drink("Boddingtons", 2.50, 'pounds');
var peroni = new Drink("Peroni", 3.50, 'euros');
var anchorSteam = new Drink("Anchor Steam", 3.50, 'dollars');

Drink.prototype.valueOf = Drink.prototype.costInDollars;
'$' + (boddingtons + peroni + anchorSteam).toFixed(2); //$11.10

Sometimes we want to coerce a complex object into a boolean, for example if the object represents a request that can end in success or failure

var SystemRequest = function(name) {
    this.name = name;
}

SystemRequest.prototype.run = function() {
    //simulate test result
    this.success = Math.random(1)>0.5;
    return this;
}

SystemRequest.prototype.valueOf = function() {
    return this.success;
}

var request1 = new SystemRequest('request1');
var request2 = new SystemRequest('request2');
var request3 = new SystemRequest('request3');

request1.run() + request2.run() + request3.run(); //2
request1.run() + request2.run() + request3.run(); //1
request1.run() + request2.run() + request3.run(); //3 (all passed!)

Here, valueOf is returning a boolean, but the final run instructions use concatenation to coerce the booleans into numbers (1 for pass, 0 for failure).

In the right situation, overriding valueOf can be a useful tool. But even if you never use it in this way, knowing how and why JavaScript chooses between default toString and valueOf methods will help you to know your code better.

About these ads

3 thoughts on “JavaScript and valueOf

    • TPReal, that’s explained in ES5 – 8.12.8 “When the [[DefaultValue]] internal method of O is called with no hint, then it behaves as if the hint were Number, unless O is a Date object (see 15.9.6), in which case it behaves as if the hint were String.”

  1. that’s not true TPReal, if you write var number = 5 + new Date() valueOf will be called.
    so, it really depends what “something” is.

    the article explains why.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s