49

Recently I found one weird line in the jQuery sources (last version 1.9.1, Sizzle package, line 129 funescape function):

funescape = function( _, escaped ) {
    var high = "0x" + escaped - 0x10000;
    // NaN means non-codepoint
    return high !== high ?            // <--- LINE 129
        escaped :
        // BMP codepoint
        high < 0 ?
            String.fromCharCode( high + 0x10000 ) :
            // Supplemental Plane codepoint (surrogate pair)
            String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
};

What is the reason to make high !== high comparison? It looks like return escaped will never be executed. Or do I miss something?

Reference: jQuery Sizzle

VisioN
  • 143,310
  • 32
  • 282
  • 281
  • 1
    maybe a bug? which is that function expected signature? I also see an unused ``_`` argument, ... – redShadow Feb 08 '13 at 12:02
  • 3
    @redShadow Well, `_` is understandable, since for some reason (possibly to preserve compatibility) the authors need to get the second argument only, besides I'd use `arguments[1]` instead. – VisioN Feb 08 '13 at 12:08
  • 5
    I think it is as the comment says, `NaN !== NaN` will **always** return `true` – Alexander Feb 08 '13 at 12:18
  • 1
    @VisioN: using `arguments` can have a fairly high performance cost. In a library like sizzle where every ms can count, not using `arguments` is likely the wiser choice http://stackoverflow.com/questions/5325554/javascript-functions-and-arguments-object-is-there-a-cost-involved – BLSully Feb 08 '13 at 16:26

3 Answers3

59

In fact it is written in the comment right above:

// NaN means non-codepoint

So it is mandatory to perform this comparison first to handle the NaN case as in JavaScript:

NaN === NaN returns false.

As pointed out by James Wiseman it is also important to know why the developer used high !== high instead of isNaN(high) which would have been clearer.

It is certainly based on performance. This test shows that a !== a is twenty times faster than isNaN(a).

zzzzBov also indicates that isNaN() could be overwritten, using !== is also more portable.

More info from Benjamin Gruenbaum:

It is also worth noting that NaN does not equal to anything else as well, and also it is not equal to anything else in an unstrict sense

And from Jan Dvorak:

Also note {valueOf:function(){return{}}} does equal itself

koopajah
  • 23,792
  • 9
  • 78
  • 104
  • 3
    It is also worth noting that NaN does not equal to anything else as well, and also it is not equal to anything else in an unstrict sense. – Benjamin Gruenbaum Feb 08 '13 at 12:13
  • 1
    Also note `{valueOf:function(){return{}}}` _does_ equal itself – John Dvorak Feb 08 '13 at 12:14
  • 2
    But why? My assumption woud dbe that `high !== high` is faster than `isNaN(high)` – James Wiseman Feb 08 '13 at 12:16
  • 1
    @JamesWiseman > this should be benchmarked to be sure and might depend on the browser. The thing is that VisioN thought (and I did too at first) that `high !== high` would **always** be false. Which it is not so this explains the point of this specific comparison! – koopajah Feb 08 '13 at 12:18
  • Several tests in the benchmark are flawed, but it clearly shows `a!==a` is _twenty times_ faster than `isNaN` – John Dvorak Feb 08 '13 at 12:23
  • 2
    It should also be noted that `isNaN` could be overwritten, using `!==` is also more portable. – zzzzBov Feb 08 '13 at 16:27
  • @zzzzBov : Added your input – koopajah Feb 08 '13 at 16:29
  • 4
    `a!==a` is not the same as `isNaN(a)`. For example `isNaN("c")` is true, but `"c" !== "c"` is false. The reason is that `isNaN(a)` tries to convert its argument to Number and checks the result of the conversion, but `a!==a` succeeds only if its argument is NaN. – fdermishin Feb 14 '13 at 17:22
  • Yes but that's the whole point of this comparison here, to handle the `NaN` case and this is what is explained in the answer – koopajah Feb 14 '13 at 17:35
  • @Jan Dvorak: Sorry, why are you pointing out that `{valueOf:function(){return{}}}` equals itself? – Randomblue Feb 14 '13 at 21:55
  • 1
    @Randomblue to indicate that the `valueOf` function is not used when comparing objects. – John Dvorak Feb 14 '13 at 21:57
  • @JanDvorak: Hum. But anyway `({valueOf:function(){return{}}} === {valueOf:function(){return{}}})` evaluates to `false`. – Randomblue Feb 14 '13 at 21:59
  • 1
    @Randomblue neither does `{}==={}` – John Dvorak Feb 14 '13 at 22:01
  • @JanDvorak: Yes, I know all of that. I'm just puzzled by your comment; I still don't understand what you were thinking. But it's no big deal. :) – Randomblue Feb 14 '13 at 22:14
13

The condition high !== high returns true, when high is NaN.I wonder why the jQuery guys did not used the much clear isNaN(high) function instead, but that was probably due to performance reasons as koopajah pointed out.

NaN (Not-a-Number) means a result that cannot be represented as a Number. It is an undeterminated number.


Why NaN === NaN returns false ?

Consider

0/0          = NaN
Math.asin(2) = NaN

You know that 0/0 is different than Math.asin(2), so why whould NaN be equal to NaN?

Jose Rui Santos
  • 15,009
  • 9
  • 58
  • 71
6

I'm piggy-backing on some of the comments here, but think this worthy information.

Some comments on the original question have suggested that this method of checking for NaN is actually much faster than isNaN()

When taken in conjunction with the following alternative to parseInt parseFloat we have a very fast way of converting to a number and checking its numeric state.

Is Subtracting Zero some sort of JavaScript performance trick?

So instead of

function Translated(val) {
    var x = parseFloat(val);
    if (!isNaN(x)) {
        alert("Not a number");
    }
}

We can have

function WTF(val) {
    var x = val - 0;
    if (x !== x) {
        alert("Not a number");
    }
}
Community
  • 1
  • 1
James Wiseman
  • 29,946
  • 17
  • 95
  • 158