Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at CFUNITED 2010 (Landsdown, VA) with:

Coercing Non-Truthy Values In JavaScript

By Ben Nadel on

I wasn't sure what to title this post. I didn't want to make the content sound exhaustive, which it isn't; but, I also didn't want it to be so vague that it garnered no interest. Mostly, I just wanted to point out something that I found in the AngularJS source code. Last week, when I was looking at how AngularJS turns a promise-resolution into an AJAX .abort() request, I saw a very interesting IF-statement that used non-truthy type coercion.

In the recent releases of AngularJS, the $http service can accept a "timeout" property that is either a number or a promise. If it's a number, it represents the timeout, in milliseconds, after which the underlying AJAX request will be aborted. If it's a promise, AngularJS will abort the AJAX request if and when the promise is resolved. In the code, AngularJS handles the type-check using this IF-statement:

  • // Check for timeout in milliseconds.
  • if ( timeout > 0 ) {
  •  
  • // ...
  •  
  • // Check for timeout as a Promise object.
  • } else if ( timeout && timeout.then ) {
  •  
  • // ...
  •  
  • }

Each condition, on its own, is rather straightforward - the "happy paths" make sense. But, what happens in the first condition when the timeout is a promise? Suddenly, we're comparing an object to a number.

In my personal experience, the vast majority of type coercion has involved treating a value as either True(ish) or False(ish). To me, this kind of coercion is easy to understand. But, when I saw the above "greater than" comparison, between an object and a number, I didn't really have any idea what to expect.

After some Googling, I found some resources that seemed to indicate that when JavaScript compares an Object to a non-Object, it does so by first converting the Object into a "primitive" using this kind of approach:

  • object.valueOf().toString()

If we apply this to an Object in JavaScript:

  • ( {} ).valueOf().toString()

... we get:

"[object Object]"

Now that we've converted it to a String, we can compare it to a Number by converting the String to a Number:

  • Number( "[object Object]" )

... which gives us:

NaN // Not a Number.

... which means that our Promise vs. Zero condition is ultimately converted into:

  • if ( NaN > 0 ) { ... }

... which is always false since NaN is never equal to anything, not even NaN.

Now, this gets really interesting when we use an Array instead of an Object:

  • Number( ( [ ] ).valueOf().toString() ); // .... Gives us 0.
  • Number( ( [ 1 ] ).valueOf().toString() ); // .... Gives us 1.
  • Number( ( [ 1, 2 ] ).valueOf().toString() ); // .... Gives us NaN.

... which means:

  • console.log( [] == 0 ); // .... true.
  • console.log( [ 1 ] == 1 ); // .... true.
  • console.log( [ 1 ] > 0 ); // .... true.

In an abstract sense, I think the IF-statement (that prompted this post) is very interesting and very clever. But, part of me also believes that it violates the "principle of least surprise." Of course, this may just represent my narrow understanding of type-coercion in JavaScript; after all, I rock truthy type-coercions like a boss. Perhaps, with time, this type of coercion (no pun intended) will also seem like second-nature.

UPDATE: It looks like I may have misinterpreted the ECMAScript spec. I have updated this blog post based on the comments by Kirill Dmitrenko, below.




Reader Comments

Another solution would have been to just reverse the if statements... if timeout has "then", we know it's a promise. Then we don't need to bother confusing programmers.

Reply to this Comment

@Jonathan,

Good point, switching the conditions would definitely have made this a bit more intuitive. But, at least it was an opportunity to dive a little deeper :)

Reply to this Comment

> After some Googling, I found some resources that seemed to indicate that
> when JavaScript compares an Object to a non-Object, it does so by first
> converting the Object into a "primitive" using this kind of approach:
> object.toString().valueOf()

No, all relative expressions only do .valueOf(), see paragraph 11.8 of Ecma-262 specification:

dmikis-osx2:~$ node
> a = {
... toString: function () { return 'a'; },
... valueOf: function () { return 2; }
... }
{ toString: [Function],
valueOf: [Function] }
> b = {
... toString: function () { return 'b'; },
... valueOf: function () { return 1; }
... }
{ toString: [Function],
valueOf: [Function] }
> a > b
true

Reply to this Comment

@Kirill,

This stuff is really hard for me to wrap my head around. I tried looking at the spec, but I have trouble understanding it. If you have an object "a", then it seems that .valueOf() is the same as the root object:

var a = {};
console.log( a === a.valueOf() ); // ... true

If that's the case, it seems that the .valueOf() doesn't get the runtime closer to a comparison. It must be doing _something_ after it gets the value in order to make the comparison, no?

It hurts my brain even more if you sub-class the Array to create an object:

var a = Object.create( Array.prototype );
a.push( 1 );
console.log( a > 0 ); // .... true

It will take a lot more time for to really let this all sink in.

Reply to this Comment

@Kirill,

After re-reading the original resources that I had, it looks like I got the method calls confused, at least in the way that I understand it. You are right - the .valueOf() would be called first:

When the [[DefaultValue]] method of O is called with hint Number, the following steps are taken:

1. Call the [[Get]] method of object O with argument "valueOf".
2. If Result(1) is not an object, go to step 5.
...
5. Call the [[Get]] method of object O with argument "toString".

I'll update the post to correct.

Reply to this Comment

Thank you very much for the information.

The first time I read about how coercion works in Javascript was with Angus Croll:

http://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/

There is also a link at the end of this article presenting something similar to your references links to bclary:

http://webreflection.blogspot.ca/2010/10/javascript-coercion-demystified.html

Some great people like Douglas Crockford advocate to never use coercion; always use strict equality comparison but Angus Croll convince me to fall in love with coercion in Javascript.

Reply to this Comment

@Ben,

My pleasure.

I personally try to avoid sophisticated type casts in my code. As John Carmack says, if something can be done wrong, it eventually will be. And, I think, type coercing is such situation.

Reply to this Comment

@Kirill,

I think I agree. With the exception that I do rather enjoy truthy type coercions, even on the empty string:

if ( "" ) { ... }

But, the rules for those kinds of coercions are very straightforward - not like the stuff of this blog posts where it's a much complicated decision tree. Crazy stuff.

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.