(Updated 2013-06-10 with some corrections and fine-tunings)
The obvious stuff
I’m not going to dwell on any of this, because it’s well-trodden territory,
but to get it out of the way: the scoping is wacky (and the
is a disaster); the comparison operators (
===) are a kludge;
and the treatment of the
this keyword is tiresome. There are many
more dark corners (see, for instance, Gary Bernhardt’s WAT), but all
of these issues can be worked around more or less easily (and in fact
CoffeeScript largely does so). There are other issues with the
language that I’d argue are harder to deal with – in some cases,
What is an object?
1 2 3 4 5 6 7 8 9
We could either use this object as-is, in which case it’s what in a class-based language we’d call a Singleton, or we can use it as the prototype for another object instance (there are several ways to accomplish this – they’re covered elsewhere, so I won’t expand).
As you can see, there’s no distinction between a ‘method’ (a member of the object which is a function) and a ‘property’. Everything is just a value indexed on a key. Nice and simple, but take a CoffeeScript instance method like this one:
This looks pretty and idiomatic: it’s an accessor method which
@thing if it doesn’t already exist, then returns it.
See the problem?
CoffeeScript’s syntax is helping to hide the fact that we’ve bound the
instance variable to the same key as the method (
this.thing). I hope
this seems obvious to you, and that you’re wondering how anyone could
be so stupid as to do such a thing. I am here to tell you that this is
something that a busy developer will do (especially one familiar with
Ruby), and that it will then manifest a very subtle bug which does not
immediately show up in unit tests. I will leave you to speculate as to
how I can be so sure about this.
Now, sure, we could be sensible and create
methods, but, you know, we’re not writing Java. We’re in a highly
dynamic language. We want nice things. Or, we could create setters and
getters using the
new ES5 syntax
– but that’s not available everywhere (pre-IE9, for instance), and, I
would argue, is too unwieldy to apply as a rule.
So what we’re left with is doing something like:
Which is … irritating. But if we want this kind of method syntax, that’s what we have to do, and we have to be vigilant that we don’t introduce mistakes like the above, because no lint tool will catch them.
Everything is an object
And that’s a good thing, right?
No. It isn’t. Imagine another hypothetical CoffeeScript method:
1 2 3 4
Spot the deliberate mistake? Busy developer has initialised
an array rather than an object. The really bad news? This method will
work almost as if it were correct. We can get every key of
an object, and therefore is just a key–value structure. We can set
(pretty much) any key on it we like, and get it back intact. But
again, subtle bugs abound; arrays are not intended to be used like
this, and things will go wrong if, for example, you start trying to
copy’s properties, expecting it to be a ‘real’ object.
Nothing is an object
Try this, in a CoffeeScript class:
1 2 3 4 5 6 7 8
bindClick has been called, what happens when we click
respondtoClick will never be called, and you’ll almost certainly get
functions. We bound the function
this.onClick to the click event;
that function has no idea that it’s also a method. Why should a value
in a key–value structure know which structure it belongs to? In the
context of the function
this is the function itself
or, sometimes, the
window object. Neither of these has a
respondToClick method, so we get an exception.
In short, we can call any function in scope, from anywhere, regardless of
whether it’s part of an ‘object’; and attaching a function to an object as
a method only binds
this correctly when the function is called in
the context of its parent object.
The correct way to do the above is of course:
We pass an anonymous function to
.on, explicitly binding
our current context using CoffeeScript’s
All of the above can be worked around or avoided; at
equivalent to Ruby’s
and then do something with it. This is, again, to do with the fact
that there’s nothing to distinguish ‘properties’ from ‘methods’ in
that doesn’t necessarily mean they should be.
It’s important to be able to do this in traditional object-oriented
design; we need it to do proper delegation, for example. It’s also a
tremendously convenient feature if you want to do metaprogramming
(e.g. adding properties or methods to an object dynamically at
runtime). There are two ways to sort-of do it right now: one is
__noSuchMethod__, which is only supported in SpiderMonkey
and likely to be deprecated; the other is the ‘official’ ECMAScript
proposal, Proxy, which is characteristically circuitous and
counterintuitive. It can be turned on in V8 (and thus in Node), but
you’d be pretty far out on a limb to use it.