Javascript gotchas thread


  • :belt_onion:

    Depending on your definition of "print"? I see text on my screen as a result of running that full block. What do you want, output to an inkjet/laser printer?

    edit - i did forget to make the numbers unique so you could see which came from which if block though.


  • 🚽 Regular

    @darkmatter said:

    Do you think it will work the same if alert is changed to echo and it's run in PHP instead of JS?

    I do and it does.


  • :belt_onion:

    @Zecc said:

    I do and it does.

    If memory serves me correctly, the instance I ran into this, the programmer had written it as
    @darkmatter said:
    if( ! $a == $b ){alert(1);}

    but what they wanted was
    @darkmatter said:
    if( ! ($a == $b) ){alert(1);}

    Not quite the same thing depending on the situation.

    Though I can't even think of a language where !a==b is the same as !(a==b)... "!" is higher precedence in everything I can think of off the top of my head.



  • define:print

    produce (books, newspapers, magazines, etc.), especially in large quantities, by a mechanical process involving the transfer of text, images, or designs to paper.



  • @Faxmachinen said:

    Okay, here's one that actually got me:How does JavaScript interpret the expression a << b == c & d?

    (a << b) == (c & d)
    a << (b == (c & d))
    ((a << b) == c) & d

    Hint: JavaScript got its operator precedence from C. And C didn't always have the && and || operators.

    Ah yes, I know there's some weirdness with JS operator precedence. That's why I always use corners. A good idea in general, too.

    @darkmatter said:

    That reminds me of this question - which ones of these if blocks will or will not print, given the following initializations to the variables? Do you think it will work the same if alert is changed to echo and it's run in PHP instead of JS?

    I'll give it a genuine shot. No looking / fiddling.

    1. no print
    2. prints
    3. prints
    4. no print

    <x/>#1 is the biggest mystery, because I don't know if ! operator has greater precedence than ==. Realistically, I'll be happy if I get 50% right.

    EDIT

    http://repl.it/X9z

    Whoa! Looks like I got 100%. Nice.


  • :belt_onion:

    @cartman82 said:

    Whoa! Looks like I got 100%. Nice.

    yep, winnar :) same in PHP (and most other languages that I happen to know) as well.


  • Discourse touched me in a no-no place

    @darkmatter said:

    same in PHP (and most other languages that I happen to know) as well.

    The main exception is Go, which does something weird with whitespace that happens to mostly work.



  • @darkmatter said:

    yep, winnar same in PHP (and most other languages that I happen to know) as well.

    Yeah I was expecting there to be some obscure gotcha. But no, this was pretty much expected. Which is rather unexpected from javascript.



  • @darkmatter said:

    If memory serves me correctly, the instance I ran into this, the programmer had written it as
    @darkmatter said:
    if( ! $a == $b ){alert(1);}

    but what they wanted was
    @darkmatter said:
    if( ! ($a == $b) ){alert(1);}

    Not quite the same thing depending on the situation.

    Though I can't even think of a language where !a==b is the same as !(a==b)... "!" is higher precedence in everything I can think of off the top of my head.

    At first, I thought you meant if ( $a != $b ){alert(1);}

    Then I remembered this is JavaScript.



  • The whitespace is just there for humans. You can put spaces between whatever you want.

    Click "format" for the canonical whitespace.


  • Discourse touched me in a no-no place

    I thought that Go used whitespace to do precedence overrides, or did they take that out again. (It was the most WTFy part of Go, to be fair.)


  • :belt_onion:

    @powerlord said:

    At first, I thought you meant if ( $a != $b ){alert(1);}

    Then I remembered this is JavaScript.

    It doesn't matter what language, pretty sure the ! will happen before the == in almost all of them. Mentally you tend to read it as $a!=$b because the whitespace implies that was what the coder was thinking, which is what threw me off too, when I saw it the first time.



  • @darkmatter said:

    It doesn't matter what language, pretty sure the ! will happen before the == in almost all of them. Mentally you tend to read it as $a!=$b because the whitespace implies that was what the coder was thinking, which is what threw me off too, when I saw it the first time.

    Well, I was attempting to imply that JS does weird things and that == and != may not work like you'd expect (just like && and || don't)... so !($a == $b) and ($a != $b) could have different results...



  • It was never a part of Go. You may be thinking of an example somewhere in the Go documentation where they said something along the lines of "this expression has the precedence that its spacing would imply".


  • :belt_onion:

    Those don't have different results that I know of.
    But (! $a == $b) vs ( $a != $b ) do give the same results in some situations, which could fool some into thinking they are the same thing.

    (0!=1) is true, (1!=1) is false, (0!=2) is true.
    !0==1 is true, !1==1 is false, but !0==2 is false.

    If you were coding by trial and error and did !0==1, then you might stop thinking it worked fine in all cases when it really doesn't.



  • Writing Javascript is like trying to make up with a pissed off girl.

    You try something, doing your best, and ask "Everything ok?"

    Girl/Javascript: "Fine."

    And then you just have to hope "Fine" means fine and not "I'mma cut you while you sleep".



  • @darkmatter said:

    (0!=1) is true, (1!=1) is false, (0!=2) is true.!0==1 is true, !1==1 is false, but !0==2 is false.

    ! coerces 0 into true, and the rest is just general weirdness with true == ...

    
       true == 2
    => false
       true == 1
    => true
       true == 0
    => false
       true == "1"
    => true
       true == "0"
    => false
       true == {}
    => false
       true == []
    => false
    

  • :belt_onion:

    yeah - i don't disagree with the results, I'm just pointing out that a naive trial-and-error coder could test a couple of those and conclude that the 2 are equivalent when they only match when you're lucky.


  • BINNED

    @KillaCoder said:

    And then you just have to hope "Fine" means fine and not "I'mma cut you while you sleep".

    "Fine" never means fine, but usually stops short of "Imma cut you while you sleep".



  • Yes, that's exactly the point.



  • @Faxmachinen said:

    Gary Bernhardt already documented a number of things in his 2012 lightning talk titled "Wat", but the video seems to have gone missing. Here's the abridged version:

    It's quite interesting if you actually know about the type conversion rules applied by the addition operator and the general parsing rules.

    > [] + [] // Array plus array
    "" // Evaluates to empty string

    Type conversion starts off by obtaining the primitive value of the left and right hand operands, as addition is only defined on primitives. To try to obtain a primitive value, first the valueOf method is run. However, for an array this returns the same array, which is still an object and not a primitive value. Because the valueOf method failed to produce a usable primitive value, the toString method is tried. For an empty array, this produces an empty string. A string is a primitive value and is returned.

    Both the left and the right hand operator yield empty strings for their primitive values. As atleast one of the operands returned is a string, both are cast to strings and the addition operator is executed as a string concatenation operation. Concatenation of two empty strings yields another empty string.

    > [] + {} // Array plus object
    [object Object] // Is object

    Again; start by obtaining the primitive values. The primitive value of the empty array is again the empty string, as before. The valueOf method of an object produces the object itself, which is not a primitive value. Again, the toString method is used, which produces "[object Object]".

    As atleast one of the operands returned is a string, both are cast to strings and the addition operator is executed as a string concatenation operation. Concatenating an empty string with "[object Object]" results in an identical "[object Object]" string.

    > {} + [] // And likewise, the reverse
    0 // Equals zero

    The parser favors treating code as a statement when it can, and not as an expression. The parser treats the whole as a statement where the leading {} is an empty code block (which is skipped) and then treats + [] as an expression formed by a unary plus operator on an empty array.

    This operator converts the array to a primitive value, producing an empty string and then casts the primitive value to a number. Casting the empty string to a number produces the numerical zero.

    If you add braces, then the whole is treated as an expression:

    > ({} + []) // Added braces turn the whole into a single expression to evaluate
    [object Object]

    This again yields concatenation of the string "[object Object]" with an empty string, again resulting in an identical "[object Object]" string.

    > {} + {} // Object plus object
    NaN // Not a Number, obviously

    The same parsing rule applies and the empty code block is skipped. The +{} expression yields NaN because the primitive value of the object literal is the string "[object Object]", which cannot be numerically represented when an attempt is made to cast it to a number.

    If you add braces, then the whole is treated as an expression:

    > ({} + {}) // Added braces turn the whole into a single expression to evaluate
    [object Object][object Object]

    As expected this yields a concatenation of two "[object Object]" strings.


    @Faxmachinen said:

    I can personally add the following:

    > (function () { return this; }).call(null) // Set a property on a null object?
    Window { ... } // Whoops, it's a global

    This is because the value assigned to this is auto-boxed into an object. The result of the boxing operation on null or undefined is the global object, which in browsers corresponds to the window object.
    (It's actually no longer possible to do this in ES5 Strict mode, which passes values through to the this keyword without any kind of boxing, specifically because of the security problem of re-exposing the global object in potentially sandboxed environments.)

    > var undefined = 12 // Set a variable that happens to be a reserved word?
    12 // Error? What error?

    undefined is not a reserved keyword. It is a property of the global object that is initially unassigned and thus is of type 'undefined'. Like most properties its value is assignable by user code and it can be shadowed by variables of the same name.

    > {} // Sanity check
    undefined // Congratulations, you're insane

    This is executed as a statement and is again interpreted as an empty code block. An empty code block yields an undefined value. If you want to get your empty literal back, turn it into an expression by adding braces:

    > ({}) // Added braces turn this into an expression
    Object { }

    The real WTF here is not JavaScript. The real WTF is people not learning about the language's rules and taking their initial interpretation as canon.



  • Very nice post. Thank you for the clarification.

    Of course, expecting anyone to actually remember these arcane rules in practice might be too optimistic. Especially since following a few simple conventions (always use ===, () to set precedence, coerce booleans etc...) can help prevent a lot of these gotchas.


  • Discourse touched me in a no-no place

    @Ragnax said:

    The real WTF here is not JavaScript. The real WTF is people not learning about the language's rules and taking their initial interpretation as canon.

    While this is technically interesting, I think it deserves nomination for a needless pedantry badge.



  • @Ragnax said:

    undefined is not a reserved keyword. It is a property of the global object that is initially unassigned and thus is of type 'undefined'. Like most properties its value is assignable by user code and it can be shadowed by variables of the same name.

    You're mistaken, it cannot be shadowed in modern browsers:

    > undefined = 12
    12
    > undefined
    undefined
    > var undefined = 12
    undefined
    > undefined
    undefined
    

    @Ragnax said:

    The real WTF here is not JavaScript. The real WTF is people not learning about the language's rules and taking their initial interpretation as canon.

    You could say the same thing about Intercal.


  • 🚽 Regular

    No, you're mistaken.

    The "undefined" property of the global object if not-configurable and not-writable, so you can't change that one.

    But nothing stops you from shadowing it by naming a variable or function argument "undefined".

    > function test(undefined){console.log(undefined); return 42;}
    <- undefined
    > test("Still WTF")
      Still WTF
    <- 42
    > (function test2(){ var undefined = "Sup?"; console.log(undefined); })()
      Sup?
    <- undefined


  • So undefined is a global property and not a reserved word? WTF. This thread has reduced my faith in JavaScript even further, and it was pretty damn low already.


  • I survived the hour long Uno hand

    @Ragnax said:

    > [] + {} // Array plus object

    [object Object] // Is object

    Again; start by obtaining the primitive values. The primitive value of the empty array is again the empty string, as before. The valueOf method of an object produces the object itself, which is not a primitive value. Again, the toString method is used, which produces "[object Object]".

    I just want to point out that for this example, assuming Ragnax is correct, the comment in the code is wrong: That's not an object being converted to a string by the console to print, that's the literal string "[object Object]".



  • Just define your own UNDEFINED constant, and initialise every variable to that constant 😄


  • :belt_onion:

    Correct, as illustrated using the following:

    [code]
    ["a","b"] + {};
    [/code]
    which outputs
    "a,b[object Object]"

    Because it is essentially
    ["a","b"].valueOf() + {}.valueOf();

    which in the case of those 2 types just calls the same as
    ["a","b"].toString() + {}.toString();



  • Sounds legit to me. But then I'd have to take your username, because I would have to kill a coder - namely the one who implemented your recommendation.


  • BINNED

    The evil ideas thread is ↘ ↕ 🔄 over there.



  • @Ragnax said:

    The real WTF here is not JavaScript. The real WTF is people not learning about the language's rules and taking their initial interpretation as canon.

    Your examples are a little extreme and outside how most people would use the language.

    But you missed some:

    {g:"F"}
    "F"
    {'g':'F'}
    SyntaxError: Unexpected token :
    

    These are the expression vs statement comment again. It is treating g: as a label so the first one is effectively a return "F". Second one chokes on the unexpected colon, since it is a string then bad syntax.

    Of course:

    ({g:'F'})
    Object {g: "F"}
    ({'g':'F'})
    Object {g: "F"}
    


  • This shit never happens in PHP. Just sayin'.


  • :belt_onion:

    I'm not sure what your point is here, that line {'g':'F'} by itself is kind of meaningless.... (edit: I realize you understand that, just not sure why anyone else would be confused by it).
    JS tries to run it as an expression, but it's not.
    var a = {'g':'F'};console.log(a);
    alert({'g':'F'});
    console.log({'g':'F'});
    are all valid.
    Try looking at it in expanded form to see how silly that line alone is:
    [code]
    {
    'g'
    :
    'F'
    }
    [/code]

    What would anyone really expect it to do? The 'g' declaration is valid, the 'F' declaration is valid, but ":" by itself is an error as it should be. Doing ({g:'F'}) is declaring it as an argument object rather than just running 3 random lines in a { } scope.



  • @darkmatter said:

    I'm not sure what your point is here, that line {'g':'F'} by itself is kind of meaningless.... (

    It's about as meaningless as adding objects to each other, especially in a language that does not allow overriding + etc. :)


  • 🚽 Regular

    @Zemm said:

    It is treating g: as a label so the first one is effectively a return "F".

    WTF! @darkmatter's following post notwithstanding, I did not see that one coming.


  • 🚽 Regular

    I feel like I'm talking with my evil twin. :-7



  • @Zecc said:

    Zemm said:
    It is treating g: as a label so the first one is effectively a return "F".

    WTF! @darkmatter's following post notwithstanding, I did not see that one coming.

    It's the same reason HTML

    <a onclick="javascript:doSomething()">
    

    is not technically correct but works without error.



  • @Zecc said:

    my evil twin. :-7

    7-: .niwtƚ livɘ ym



  • Gimme the sockpuppet finder badge.


  • :belt_onion:

    @Zemm's avatar just needs a reverse shadow in the background of his reverse Z



  • CBA to do actual work for the last half-hour on a Friday. So here you go.

    http://i.imgur.com/FCadk4w.png



  • Let's see if I can download and upload via mobile...


  • :belt_onion:

    Win.

    @Zemm said:

    @Zecc said:
    my evil twin. :-7

    7-: .niwt livɘ ym



  • So which one's the mattress from Sqornshellous Zeta?



  • @Arantor said:

    Sqornshellous Zeta?

    Voon



  • Fantastic. Someone gets the reference 😃



  • That better not be another Morningwood Crescent thing.


  • Discourse touched me in a no-no place

    Hitchhiker's Guide to the Galaxy.



  • Oh crap, that thing that's three feet from me.


Log in to reply