Categories
Uncategorized

Principle Of Least Surprise: toFixed() in JavaScript

The “Principle Of Least Surprise” ( sometimes referred to as “Principle Of Least Astonishment” ) as described by M. F. Cowlishaw in the paper “The design of the REXX language” in 1984:

It is difficult to define exactly how to meet user expectations, but it helps to ask the question: Could there be a high astonishment factor associated with the new feature? If a feature is accidentally misapplied by the user and causes what appears to him to be an unpredictable result, that feature has a high astonishment factor and is therefore undesirable. If a necessary feature has a high astonishment factor, it may be necessary to redesign the feature.

While being a bit on the squishy side, this still a very useful principle.

I recently ran into a violation of this principle while using Number.prototype.toFixed() in JavaScript.

The code was capturing a time value using performance.now(). Since it was already counting in milliseconds, and I was exposing the result as JSON, I didn’t really need 15 decimal places. No problem, I’ll use toFixed() to reduce it to 3 decimal places. Here is a simplified version of the code and the result:

var now = performance.now().toFixed( 3 );
var share = JSON.stringify( { now: now } );


// share is now {"now": "8.618"}

That is the point where I realized something wasn’t right. When calling performance.now() you get back a double ( DOMHighResTimeStamp type ). Why then was it coming out as a string in the JSON?

It turns out that toFixed() “formats a number using fixed-point notation” by returning a string. The MDN docs do indicate that the return value is a string, but I contend that this is completely unexpected. In order to keep the value as a double, you need to wrap the whole thing in parseFloat():

var now = parseFloat( performance.now().toFixed( 3 ) );
var share = JSON.stringify( { now: now } );


// share is now {"now": 8.618}

It would be clearer, and less work ( parseFloat() can be a hit in performance ), if toFixed() preserved the variable type and returned a double.