Friday, April 2nd, 2010

A very detailed look at object to primitive conversions

Category: JavaScript

<>p>Ben Cherry continues to write nice detailed posts on JavaScript. This time he delves into the magic of Object – primitive conversions.

He post has some good fodder for wtfjs as he shows details of toString, valueOf, and implicit fun. Take a simple example like population:

javascript
< view plain text >
  1. function population(country, pop) {
  2.     return {
  3.         country: country,
  4.         pop: pop,
  5.  
  6.         toString: function () {
  7.             return "[Population " +  
  8.                 "\"" + country + "\" " +
  9.                 pop +
  10.             "]";
  11.         },
  12.  
  13.         valueOf: function () {
  14.             return pop;
  15.         }
  16.     };
  17. }
  18.  
  19. var america_pop = population("USA", 350e6);
  20. var mexico_pop = population("Mexico", 200e6);
  21. var canada_pop = population("Canada", 200e6);
  22.  
  23. alert(america_pop); // [Population "USA" 350000000
  24.  
  25. var north_america_pop = america_pop + mexico_pop + canada_pop;
  26.  
  27. alert(north_america_pop); // 750000000

And then we get too the bad side of:

javascript
< view plain text >
  1. var foo = {
  2.     toString: function () {
  3.         return 5;
  4.     },
  5.     valueOf: function () {
  6.         return "foo";
  7.     }
  8. };
  9. alert(foo.toString() + 1); // 6 (bad!)
  10. alert(foo + 1); // "foo1" (no good!)
  11. alert(+foo); // NaN (the worst!)

After a performance comparison he ends with a conclusion:

  • Implement toString and valueOf on your commonly-reused objects. They can help you write clearer, more concise code, and make debugging easier too.
  • All browsers implement object-to-primitive conversion according to the specification, so you can safely consult it for more detail.
  • When performance is important, always try to call your type-conversion methods directly, instead of relying on JavaScript’s implicit calls.

Related Content:

  • Part 2 - A Detailed Look at SOAP
    In Part One of the hands-on Web services tutorial we introduced the basic concepts behind web services, including SOAP and WSDL. We showed how to...
  • ICommerce Design Strategies
    GemStone has been in the distributed object world for well over a decade, first with Smalltalk and now with Java. This document and FoodSmart, the...
  • The Working Developer's Guide to Java Bytecode
    In this article, we're going to examine the JVM bytecode set, disassemble some code to see how it works, and play with some tools that allow us to...
  • TSS Relaunches on Tapestry
    From its inception in May 2000, TheServerSide.com has been an exceptionally successful information portal site, receiving upwards of seven million...
  • Part 1 - Developing my first Web Service in 30 minutes
    You've heard the hype, and your head is probably dizzy from all the acronyms. So just what are Web Services, and how can you use them? This series of...

Posted by Dion Almaer at 8:10 am
8 Comments

+++--
3.8 rating from 14 votes

8 Comments »

Comments feed TrackBack URI

For every toString method, I always return as a new String(someValue) to force it as a string all the time. In the case above for “foo”, the toString return should have been return new String(5);

Unfortunately, in the case of valueOf, that can be of any type (string, integer, float, etc…) and we end up hitting the typical Javascript issues as described above.

On a side note: I have stopped using “+” to concatenate strings and instead use array joins.

so that:
“[Population " + "\"" + country + "\" " + pop + "]“;

becomes:
["[Population ", "\"", country, "\" ", pop, "]“].join(”);

… and again that still does not help the alert(foo + 1); issue.

Good post!

Comment by albeik — April 2, 2010

Could you derive further benefits from this approach by prototyping the valueOf and toString methods?

Comment by atomictim — April 2, 2010

atomictim: There’s no added benefit in terms of capabilities if you put toString on the prototype. It’s still going to be called with the same this, and there can still be only one. It’s just a matter of personal preference or code structure. If you’re using inheritance, then it definitely makes sense to have it there. If not, either way is fine, but I usually opt to avoid the prototype (it’s marginally slower to resolve properties off the prototype chain).

albeik: Yep, if there’s any doubt, Array.join is your best bet.

Comment by bcherry — April 2, 2010

The example is very weak. This would be a much better improvement:


function Population(country, pop) {
this.country = country;
this.pop = pop;
}
Population.prototype.toString = function () {
return "[Population " + '"' +
this.country + "' " + this.pop + "]";
};
Population.prototype.valueOf = function () {
return this.pop;
}

@atomictim :
Yes

@bcherry:
That’s crap. Your wasting memory and creating a duplicate of code unnecessarily on every creation. Its not a matter of opinion. Also, it’s not “marginally slower” to lookup a prototype method, its insignificantly slower.

@albeik:
Unless you are concatenating a significant number of strings, I doubt you will see a benefit to using an array instead.

Comment by TNO — April 2, 2010

More of these kinds of posts please.
.
@TNO Having trouble seeing how your example differs – apart form use of prototoype/constructor pattern which adds nothing to our understanding of the issue

Comment by AngusC — April 2, 2010

@TNO This isn’t an article about inheritance patterns, so I didn’t use any inheritance patterns. Not sure what adding the prototype and using a constructor function does for the simple example. That aside, I generally avoid using constructors and prototypes unless I’m expecting to make >10 instances. There just isn’t a significant difference, and the non-prototypal version is cleaner and easier for most people to understand, IMO.

@AngusC glad you liked it :) I’ve got a handful of other JS language posts, but this is the most detailed I’ve done.

Comment by bcherry — April 2, 2010

@AngusC
First off the function is named properly. Secondly, there is no reason whatsoever to create 3 new objects per call. If anything I would call the original example an anti-pattern. The understanding of the issue should be understanding how to create objects properly.

@bcherry
“This isn’t an article about inheritance patterns, so I didn’t use any inheritance patterns.”
.
So it’s ok to right bad code because it wasn’t your focus?
.
Not sure what adding the prototype and using a constructor function does for the simple example.
.
You’re creating object instances with shared functionality, there is no reason not to use inheritance either as a static pointer, or through the prototype. You did neither.
.
That aside, I generally avoid using constructors and prototypes unless I’m expecting to make >10 instances.
.
In other words you realize that your approach doesn’t scale, but you avoid doing the better approach anyway?
.
There just isn’t a significant difference,
.
Creating 3 objects per call vs 1 object per call isn’t a significant difference?
.
, and the non-prototypal version is cleaner and easier for most people to understand, IMO.
.
Just because it’s “easier” doesn’t make it right.

Comment by TNO — April 2, 2010

The fastest way to concatenate strings is the concat method itself, which makes sense when there are more than 3 strings to concatenate

value.concat(a, b, c, what + ever, d, e, f);

The array creation plus join invocation “cannot” be better …

a toString method should return a string, an explicit cast is superfluous but in any case “” + value or just String(value) is preferred over the new String(value), no need to create a String instance when we need a primitive, don’t you agree?

it’s up to developers return a non string via toString, it’s a performance issue if each toString invocation has to perform extra operations.

In this particular example, the string can be easily created once in the function body, and returned for each invocation … et voila’, best performances per invocation since the example does not use dynamic access (this.country, this.pop)

var $toString = “population …” + what + ever;

toString: function () {
return $toString;
}

The second example with NaN clearly shows a bad usage of toString and valueOf so it’s good to know what should be used for what.

About valueOf, it is important to understand that it can be invoked in different ways, as example:
// shows “null”
alert(“” + {valueOf:function(){return null}});

// shows false
alert(null == {valueOf:function(){return null}});

// while this shows true
alert(1 == {valueOf:function(){return 1}});

In certain cases, valueOf or toString could become truly handy to compare objects, e.g.

var o1 = {valueOf:function(){return 1}};
var o2 = {valueOf:function(){return 2}};

alert(o1 < o2); // true
alert(o2 < o1); // false
alert(1 + o1 == o2); // true again

In Date objects, valueOf returns getTime value so that the minimum delay could be measured in this way:
var d1 = new Date;
while(!(new Date-d1));
var minimumTimeout = new Date – d1;
alert(minimumTimeout);

which should produce 1 in common browsers, 15 or 15+1 in IE

Just my 2 cents after some span clean up :-)

Comment by webreflection — April 3, 2010

Leave a comment

You must be logged in to post a comment.