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...
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.)
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.
I think it's because you're more likely to encounter NaN in JavaScript due to the lack of real integers (er, should I say true integers? Everything is floating-point, anyway).
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.
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.
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).
I recently added my own syntax rule to JavaScript mode in Emacs to highlight lone { at the beginning of line (with or without trailing whitespaces). After your comment, I think about adding some more highlights for with, == and !=.
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);
}
Unfortunately it wasn't there from day one. It was only specified a year or two ago (thus IE 8 and Opera 11.50 didn't have it, and apparently Safari 5.1 still doesn't) so you have to be prepared to check for it and roll your own.
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.
Sure there are - why are so many Crockford pronouncements taken as a signal to stop thinking?
Notice that his "with is bad" examples (which are usually bad examples and bad usages [1]) always involve contrived examples of potentially ambiguous assignment - one of the more useful ways to use `with` is with objects whose properties are DSL-like functions, named in such a way that there's no mistaking their origin and with no ambiguous assignment involved.
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?
"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...
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.
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`.
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.
* "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:
* "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": * "Fixing Arguments" -- Function arguments are always available as an array instead of an Arguments object. * "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...