55

Is there any modern browser that raises exceptions on NaN propagation (ie multiplying or adding a number to NaN), or that can be configured to do so?

Silent NaN propagation is an awful and insidious source of bugs, and I'd love to be able to detect them early, even at a performance penalty.


Here's an example bug that use strict, jshint et al. wouldn't pick up:

object = new MyObject();
object.position.x = 0;
object.position.y = 10;

// ... lots of code

var newPosition = object.position + 1; // <- this is an error, and should
                                       //    have been object.position.x
                                       //    however it fails *silently*,
                                       //    rather than loudly

newPosition *= 2;                      // <- this doesn't raise any errors either.
                                       //    this code is actually ok if the
                                       //    previous line had been correct

Note: The TypeScript compiler is able to detect errors like the above, even in JS code, if type inference succeeds.

kibibu
  • 6,115
  • 1
  • 35
  • 41

5 Answers5

39

To answer the question as asked:

Is there any modern browser that raises exceptions on NaN propagation (ie multiplying or adding a number to NaN), or that can be configured to do so?

No. Javascript is a very forgiving language and doesn't care if you want to multiply Math.PI by 'potato' (hint: it's NaN). It's just one of the bad parts (or good parts, depending on your perspective) about the language that us developers have to deal with.

Addressing the bug that has you asking this question (presumably), using getters and setters on your Objects is one solid way to enforce this and also keep you from making mistakes like this.

Rob M.
  • 35,491
  • 6
  • 51
  • 50
  • I'm accepting this answer as it most directly answers the question. However, a @Jerry's answer solves for the case of performing numeric actions using an object, which addresses a large number of cases for the actual problem quite well (except for multiplying `undefined` * 3 or similar) – kibibu Dec 12 '13 at 05:31
30

Code below might help you.

To solve this problem fully, I think we need something like operator reload. We can reload operators like '+ - / *', and check if the operand is number, if not, then throw Error.

As a partial solution, when JavaScript does an operation like 'a + b', it will call the valueOf method which inherits from Object.prototype, we can rewrite Object.prototype.valueOf.

Object.prototype.originalValueOf = Object.prototype.valueOf;

Object.prototype.valueOf = function() {
  if (typeof this !== 'number') {
    throw new Error('Object is not a Number');
  }

  return this.originalValueOf();
}

var a = 1 + 2; // -> works
console.log(a); // -> 3

var b = {};
var c = b + 2; // -> will throw an Error

(hint: You can remove the code in production, and add it into your developing environment.)

Jerry
  • 909
  • 7
  • 24
  • This is ideal! What browser does this work on? Chrome and Safari at least don't allow replacing native prototypes – kibibu Dec 12 '13 at 05:10
  • 4
    this would affect any object not only numbers and therefore is not something you want to use :( –  Dec 12 '13 at 05:10
  • @Wesabi, I'm happy to have the perf hit and can work around the limitations if it works. Unfortunately I can't make it happen. – kibibu Dec 12 '13 at 05:12
  • @Wesabi You can remove this in production, and add it when you are developing. – Jerry Dec 12 '13 at 05:14
  • 3
    Adding this to Chrome then running my code crashes both the tab and the dev tools. Still exploring though! – kibibu Dec 12 '13 at 05:49
  • 13
    nasty, nasty idea! `.valueOf()` isn't just used by numbers! – Alnitak Dec 12 '13 at 10:40
  • @kibibu Probably because this is also called on both values in string concatenation: `"something " + "else"` – Izkata Dec 12 '13 at 12:59
  • You may want to use `Object.defineProperty` to ensure that `originalValueOf` isn't enumerable. Otherwise you'll get weird behavior like this: `var arr = new Array(); for (x in arr) { console.log(x) }; //=> originalValueOf` – Ajedi32 Dec 12 '13 at 14:38
  • @Ajedi32 Yeah in practice I actually set this up in a function, closing over a var. I'll update it. – kibibu Dec 16 '13 at 02:18
11

the cleanest way to do that is having a short handy function that validates the expression's result every time

i know that's not the answer you are looking for but this is the javascript's nature and you can't change it sadly

function v(a){ if(isNaN(a)) throw "error"; return a; }
var test = v(100 * "abc");
3

I know this is an old thread but I think there may be a super simple answer to your question. You can either create a function for the below statement or add it inline.

JavaScript has some unique behaviors for certain values. One of these unique behaviors revolves around the not-a-number value or NaN. The value of NaN will not compare equal to any other value including NaN itself. For this reason the following statement:

if(x != x) {
    throw "Value is NaN!";
}

will hold true if and only if the value of x is NaN.

War10ck
  • 12,387
  • 7
  • 41
  • 54
  • 1
    the `isNaN` function is a better way to check this, but the question is asking about a way to break in the debugger whenever a NaN is produced. – nornagon Oct 08 '21 at 02:30
2

I think this is a situation where getter and setter comes in handy.

Following is a psuedo code example just to give you an idea.

//Inside your Object class code.
function getPosition() {

  //You don't want the property "position" to be NaN, right?
  if(isNaN(this.position)) 
    throws "NaN is not a correct numerical value!";

  return this.position;

}


//Somewhere in your code
var newPosition = object.getPosition() + 1; //raises an exception.

I think this is better than implementing fake operator overloading and make things more complicated.

Dan Ross
  • 3,596
  • 4
  • 31
  • 60
Jason
  • 1,298
  • 1
  • 16
  • 27
  • I agree about getters and setters, but you shouldn't expect `isNaN` to be a method of anything and calling `anything.isNaN()` will throw an exception automatically – Rob M. Dec 12 '13 at 04:45
  • Yeah, sorry. But this is just a psuedo code to give you an idea. – Jason Dec 12 '13 at 04:46
  • `isFinite` is another handy function. Sometimes you want to prevent passing in things like `Infinity` or `-Infinity`! – wprl Dec 12 '13 at 05:04