
JavaScript: Warts and workarounds - fogus
http://matt.might.net/articles/javascript-warts/
======
jashkenas
For the record, CoffeeScript tries to help ameliorate _all_ of these "warts".

* "Hoisting under the hood" -- Function declarations vs. function expressions don't get you into hoisting trouble, because there are no function declarations, and every value is an expression.

* Explicit block scopes can be (more easily) created with:
    
    
        do (x, y) ->
          # here, x and y are captured in a new scope.
    

* "What does 'this' mean?" -- The value of "this" can be fixed lexically, by using the bound function (fat arrow) =>, instead of the normal function arrow: ->. Look ma, no "var that" or "var self":
    
    
        $.getJSON url, (resp) =>
          this.setResponse resp
    

* "Fixing Arguments" -- Function arguments are always available as an array instead of an Arguments object.
    
    
        myVariadicFunction = (args...) ->
          # here, "args" is an Array.
    

* "Avoiding truthiness" -- There is no "==" and "!=" in CoffeeScript, all equality is strict equality. For the one case where double equals _is_ useful in JS, you have the existential operator...
    
    
      if a is b
        # here, a === b.

------
dherman
I'm a friend of Matt's, and I enjoy his blog posts, but I'm afraid this post
is full of bad advice and misinformation. He claims that `with` followed by an
object literal is fully statically analyzable (it's not, because of
prototypes), and he recommends using it without considering how badly it
deoptimizes in all modern JS engines.

And spicyj is right that NaN is not equal to itself in most languages because
that's how IEEE754 specifies it. In particular, this is true in Scheme, one of
Matt's favorite languages. ;-P

(Also, depending on what he means by "true equality," his `equal` function
fails to distinguish -0 and 0, which are distinct values in the language.
However, I suspect those are best treated as the same value. It's extremely
rare to want to treat -0 as if it exists at all.)

------
spicyj
_Yet, there is still a value x such that x != x and x !== x._

 _That value is NaN._

 _If true equality matters, use a helper function:_

    
    
      function equal(a, b) {
        if (a === b)
          return true ;
        if (isNaN(a) && isNaN(b))
          return true ;
        return false
      }
    

People keep repeating this as a strangeness of JavaScript, whereas in fact
this is standard floating-point behavior, the same as you'll see in any other
language (C, Java, Python, really everything). In JavaScript, you almost
always just want a === b, not a function that checks for NaN.

~~~
asynchrony
Since NaN != NaN you could also do (a === b) || ((a != a) && (b != b))

~~~
underwater
I don't know why you'd use a != a instead of isNaN(a). The former hides what
you're doing. Though treating two NaN values as equal doesn't even make sense
in the first place.

------
finnw
The first code sample in the article is wrong:

> _In most curly-braced languages, blocks delineate lexical scope. For
> example, in C or Java:_
    
    
        { 
            int i = 13 ;
            { 
               int i = 42 ; 
               print(i) ;
            }
            print(i) ;
        }
    

In fact this will not compile in Java.

~~~
ufo
I am not very familiar with Java details. Can you explain why this doesn't
compile?

~~~
finnw
It's this part:

    
    
        int i;
        {
            int i;
        }
    

Java does not allow you to shadow one local variable with another.

This is ok:

    
    
        {
            int i;
        }
        int i;
    

because the scopes do not overlap.

This is ok too:

    
    
        int i;
        {
            class LocalClass {
                public void method() {
                    int i;
                }
            }
        }

------
spicyj
One of the strangest things about == is that although it's commutative, it's
not transitive:

    
    
      0 == '0'  // true
      0 == ''   // true
      '0' == '' // false

~~~
jaysoo
Is it really that strange?

Those statements are basically (as per ECMA-262 11.9.3):

0 === Number('0') 0 === Number('') '0' === ''

------
greggman
Honestly, I don't agree that most of these are warts. In particular function
scoping vs block scoping, get THE F over it. Gees.

Sure when I first started JavaScript it bit me exactly once. Since then, I got
use to it. It's a different language, every language has its quirks. I'm sure
if you started with JavaScript the fact that many other languages DON'T have
block scope might upset you. You know what else doesn't have block scope?
PYTHON!

    
    
        def foo():
          if True:
            x = 123
          print x
    

prints 123. In C, Java, C++ you'd get an error that x doesn't exist.

    
    
        void foo() {
          if (true)
            int x = 123
          printf("%d\n", x);
        }
    
        error: ‘x’ was not declared in this scope
    

4 meanings of this?

The fact is it gives you huge flexibility. By default, lots of libraries add
names to the global object (for browsers that's 'window') but because you can
control 'this' for all functions you can force any library to install itself
to some other object.

Can you do that in C, C++ or Java? As far as I know the only way to fix that
in those languages is to edit a bazillion source files and change namespaces
of you're lucky enough to have them.

Truthiness?

Again it's just different not broken. Lots of dynamic languages have strange
kinds of conversions. Learn them and get over it. The only people this messes
up are people expecting it to behave like Java or C/C++. It's not Java or C++.
It's not JavaScript's fault you're too stubborn or set in your ways to try it
a different way.

------
ax
1\. Don't use with. It's hard to reason about, and it breaks many compiler
optimizations (you're adding a dynamic object into the scope chain).

2\. Just don't ever use == or !=. Forget they exist. Done.

~~~
PotatoEngineer
I've always found == to be very useful when comparing undefined, because
undefined == null. It's very rare that I want to treat undefined and null
separately (unless one or the other means there was an error earlier).

~~~
funksta
That's the only reasonable use for ==, IMO.

------
jayferd
It's also impossible to instantiate an object with `new` and varargs.

    
    
        new Breakfast('bacon', 'eggs') // great
        new Breakfast.apply(null, ['bacon', 'eggs']) // b0rk

~~~
eldude
This works:

    
    
        var a = new (Breakfast.bind.apply(Breakfast, [null, 'bacon', 'eggs']))
        a instanceof Breakfast // true
    

Or...

    
    
        var a = new (Function.bind.apply(Breakfast, [null].concat(['bacon', 'eggs'])))
        a instanceof Breakfast // true
    

UPDATE (for kicks):

Or... (works with Crockford's Object.create)

    
    
        var a = Object.create(Breakfast.prototype);
        Breakfast.apply(a, ['bacon', 'eggs']);
        a instanceof Breakfast // true

~~~
jayferd
Awesome. I do notice a gigantic warning on MDN to not rely on this behavior,
though:
[https://developer.mozilla.org/en/JavaScript/Reference/Global...](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind#section_9)

In p.js (github.com/jayferd/pjs), I used this pattern/workaround:

    
    
        var Breakfast = function(args) {
          if (!(this instanceof Breakfast)) return new Breakfast(arguments);
          if (args && typeof this.init === 'function') this.init.apply(this, args);
        }
    

So these would be equivalent:

    
    
        Breakfast('bacon', 'eggs')
        new Breakfast(['bacon', 'eggs'])
    

Whereas `new Breakfast` would return an "uninitialized" object as in
Object.create.

~~~
eldude
I used a similar pattern once, except _generically_ as follows:

    
    
        function Breakfast(my, args) {
          if (!(this instanceof arguments.callee)) return new arguments.callee(arguments);
          if (Object.prototype.toString.call(arguments[0]) === '[object Arguments]') arguments.callee.apply(this, args);
        }

------
mistercow
That use of `with` makes me squeamish as hell.

Also, on the topic of the "eta" function, it's odd that the author seems
unaware of existing implementations of `bind`.

~~~
invisiblefunnel
Crockford warns against ever using `with` in a talk he gave last year.
Discussion on `with` at 19m58s: [http://ontwik.com/javascript/douglas-
crockford-javascript-pr...](http://ontwik.com/javascript/douglas-crockford-
javascript-programming-style-and-your-brain/)

Edit: He says it's not that `with` isn't useful, but that there is never a
case where it isn't confusing.

~~~
mistercow
That is very well put. It is a shame that JS got its scoping so very upside-
down, because it means that tiny changes in code can cause incredibly
confusing and initially undetectable bugs. `with` is basically an infected
bandaid on a broken system. It seems like it's helping until you notice the
smell.

------
underwater
_The way around this scoping issue is to declare `that`._

It's simpler to bind the function to the current object.
[https://developer.mozilla.org/en/JavaScript/Reference/Global...](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind)

------
firefoxman1
Something sort of random I just noticed: you can wrap stuff in {} to make
blocks without an actual block statement...just a random block of code. That
may be pretty useful once the _let_ keyword becomes widely available, and
could be used now to maybe organize lots of code?

~~~
jQueryIsAwesome
Not really, wrapping the code in {} makes no difference whatsoever, but a
anonymous function executed after creation does (function(){ /* code */ })()

In another note, the article says that '' == false yields false but in Chrome
and Firefox it returns true.

~~~
elehack
Actually, wrapping the code in {} _does_ make a difference with 'let'. A 'let'
in braces does not escape the braces.

~~~
jQueryIsAwesome
Well, the "let" keyword still doesn't work on any major browser so is like it
doesn't even exist yet.

~~~
bzbarsky
"let" has worked in Firefox for years; support for it first shipped in Firefox
2 back in 2006. You do have to opt in to it by putting
type="application/javascript;version=1.7" on your script tag, since the
feature is not backwards-compatible and hence couldn't be added to existing
scripts without possibly breaking them.

JS 1.7 in general added a bunch of neat features (array comprehensions,
destructuring assignment, etc) that are only now slowly making their way to
specifications after some foot-dragging by certain parties. See
[https://developer.mozilla.org/en/JavaScript/New_in_JavaScrip...](https://developer.mozilla.org/en/JavaScript/New_in_JavaScript/1.7)

------
adambard
My rule of thumb is, as soon as it gets to the point where I need to use
"this", I find a library that abstracts that whole business away.

I've been there and it sucks. I say leave it to someone with the expertise to
use "this" in Javascript without shooting themselves in both feet, at least
for browser-side work.

~~~
sjs
Learn the language. You can't just outsource understanding the language you
write code in. Even if you just mash together some jQuery you have to
understand the semantics for `this` or you're going to trip over them later.

It's not even that complicated. Honestly, just put in 10 minutes one day to
learn how it behaves. Even if you end up just always assigning a new variable
(e.g. var self = this) it's better than putting your fingers in your ears and
closing your eyes when you see `this`.

~~~
justinlau
Most of the JS development that I've seen happen is done by people holding
their nose and refusing to properly learn the language's idioms, or by people
that wish they could be writing in another language that abuses JS as a
glorified bytecode interpreter.

I agree that it's a pretty awful language. I wish it weren't the only choice
in web dev (and resent the HTML5 zealots that chafe at any attempt to go
beyond it with new languages - LOL, as if HTML5 was a real standard to begin
with!) But it's there, that's reality, and the good programmers need to deal
with it.

Coffeescript makes it a modicum less terrible, but it's Yet Another Language
to learn, and people that are already burdened with maintaining more than just
the FE stack, like myself, are getting pretty tired of context-switching
between all these languages and mini-frameworks-with-adorable-nonsensical-
names.

~~~
hello_moto
BAM, the best comment from a regular, passionate, and smart developer.

JS developers, understand this: the language is not elegant.

