Hacker News new | past | comments | ask | show | jobs | submit login
Javascript null surprise
35 points by gruseom on Feb 22, 2008 | hide | past | favorite | 20 comments
I just encountered this while tracking down a bug in some Javascript (verified in FF and IE):

   null < 0 || null == 0    -> false
but

   null <= 0    -> true
Would you expect that? I didn't. Obviously <= is implemented as !>. Time to look up all uses of <= and >= in my code...

p.s. Is this too trivial to post here? Too specialized? I'm curious.




[Rhino book] > When null is used in a Boolean context, it converts to false. When used in a numeric context, it converts to 0. And when used in a string context, it converts to "null".


So I guess the confusing bit is that null==0 is Boolean, rather than numeric, context.


But isn't the context a Boolean expression, so the 0 would be converted to a Boolean as well?


== needs two arguments of the same type. If they're both strings, it's a string compare. If they're objects, it does whatever it does for objects (identity, I think). If they're numbers, then it's a numeric comparison.

If it's given two objects of different types, then it has to coerce one of them. In this case, I would expect it to coerce null to 0, then compare 0==0.

Your way would have it coerce 0 to false and null to false, so it's still equal, but it has to coerce two things.

I can sort of see the logic in this though, since a lot of other languages (SQL) have the rule that nothing is allowed to be equal to null.


Yes I think you are right about that, too early in the morning for me...

Makes me wonder what the rules are for coercion though, that is does it pick null to coerce or 0?


I just experimented a bit with Rhino ( http://www.mozilla.org/rhino/ ) and I got nothin'. I had thought it might be that it coerces the right hand side to match the left's type, but it doesn't since:

null==0 || 0==null -> false

It's weird for number/string comparisons, too. "12" is greater than "102" (lexical ordering, "2" > "0") but less than 102 (coerces "12" to number).

It seems like if one side is a number, then it coerces the other one to number as well, unless the other is null.

Which makes sense given what it was designed for, since any attribute you pull in from a DOM element will be a string, and you want to make forgetting to convert safe.


Thanks for the insightful reply.


Do we have an explanation yet for why

   null == 0  -> false?
It can't be because operands are converted to numbers or booleans, since 0 == 0 and false == false are both true.


How about this one...

    js> r = RegExp("asdf", "g"); 
    /asdf/g 
    js> r.test("asdf") 
    true 
    js> r.test("asdf") 
    false 
    js> r.test("asdf") 
    true 
    js> r.test("asdf") 
    false
Surprisingly this is not a bug, though I was certain it was at first. When using the global flag the RegExp object will keep track of the last matching character position and start the next comparison at that position (after the 4th character, in this case) which results in this seemingly incorrect alternation between "asdf" matching "asdf". Removing the global flag produces correct results.


This is definitely on-topic. We've had JavaScript gotchas before ( http://news.ycombinator.com/item?id=115914 ).


Interesting and not what I would have expected. I just confirmed it in Opera. Also, null<=null is true.

You can use the following javascript link to confirm this:

(Note - I can't ever remember how to embed links properly in YC... is there a reference somewhere?)

javascript:alert('null < 0: '+(null< 0)+'\nnull ==0: '+(null==0)+'\nnull > 0: '+(null>0)+'\nnull<=0:' + (null<=0) + '\nnull < null: '+(null<null)+'\nnull == null: '+(null==null) + '\nnull > null: '+(null>null)+'\nnull<=null: ' + (null<=null));


In Safari 3 and Firefox 1.5, at least, null == null, so I would expect null <= null.

Both Safari 3 and Firefox 1.5 do the decidedly unexpected null <= 0 thing.


JSLint ( http://www.jslint.com ) won't catch this, either. It will complain about using == or !=, but it doesn't say anything about other comparisons.


Okay... I have another one.

In Python:

  >>> -1 % 3
  2
In Javascript:

  >>> -1 % 3
  -1


That is actually an error in the python implementation. According to the language reference: "(a/b)*b + (a%b) = a" where the (a/b) step is rounded towards 0. This implies the if a is negative, (a%b) should also be negative. Note that this modulus standard is also used for the C, C++, and JAVA standard. Here, its python's mess up.


This implies the if a is negative, (a%b) should also be negative.

No, python is giving you the Modulus after division (i.e. a "distance" which should be positive). If you interpret it that way everything is fine:

  >>> a = -1
  >>> b = 3
  >>> (a/b)*b
  -3
  >>> (a%b)
  2
  >>> (a/b)*b + (a%b)
  -1
Perhaps you're thinking of the Remainder?

Edit: it seems there isn't much consensus about what to do when a or b are negative. Wikipedia gives a helpful chart here how different languages choose to implement this: http://en.wikipedia.org/wiki/Modulo_operation


True. I checked the python reference again and it re-adjusts the modulo to have the same sign as the 2nd operand.

>>> 1 % -3 -2 >>> -1 % -3 -1


How about this surprise which caught me out a couple of days ago, try:

function hello () {

    return
    {
       value : 10
    };
}

window.alert (hello ().value);

It burps in FF and IE...


This happens when putting a newline between return and any value.

This is actually a result of JavaScript's lax support of semi-colons. Semi-colons are generally not enforced in JavaScript, but this leads to some ambiguous cases. Your case may not seem ambiguous, but the following code snippet will show what is actually going on:

if (i==5) return

i += 3;

As you can see, there are 2 possible meanings for this when there is no explicit semi-colon after the return. It can either mean "if i equals 5 return, else add 3 to i" or it could mean, "if i equals 5 return i plus 3". JavaScript does not try to discern which is "more correct" (since there are no return types on functions so this would be a very difficult task), but rather defaults to "floating returns" meaning "return;".


I know, I mentioned it as the new object because it's one of the few situations I know of where you might use a newline after the return keyword.

I understand what you mean, in this case though it would be helpful if the interpreter detects the statement as being not reachable since it's after the return.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: