Writing Eloquent JavaScript Without CoffeeScript
oscargodson.com
oscargodson.com
> The examples on the CoffeeScript site are purposely written
> to be ugly to make CoffeeScript look even prettier.
... not at all. The side-by-side examples on CoffeeScript.org are showing you the compiled output. The point is to demonstrate -- although it's certainly far from perfect -- how CoffeeScript features can be compiled into clean JavaScript. Really, our goal is to make the JS look as nice as possible.CoffeeScript on the left, compiled JavaScript output on the right.
Maybe the two halves should have the headers directly aligned above them:
> CoffeeScript
> The JavaScript that it compiles to.
Other than that, it's hard to make it any clearer for its intended audience (people who know JS and know what the words "compiled" and "output" mean).
Still, I'm tempted to upvote the OP as good Friday satire.
When I visit the site and I see a table with CoffeeScript on one side and Javascript on the other, my brain sees just enough to verify that the expected elements are all there before it says, "Yep - there is the comparison you wanted".
So while it does clearly state that the JavaScript is the output of the tool, my brain has already decided that it is exactly what I was expecting, and it takes me a second to re-adjust my thoughts.
Ironically, if the output looked less "normal" I probably wouldn't have the same problem, but it is formatted nicely and is good enough that subconsciously my brain tells me "equivalent syntax" and not "compiler output".
It doesn't do enough to warrant learning the abstraction syntax.
Now my mind's eye just totally skips variable declaration when visually code parsing, variable declaration is meaningless drivel that exists only to stop typos (a good purpose).
I had an opinion on the layout of them back then because I wasn't a good enough programmer yet to have an opinion on anything else, I had opinions on things that didn't matter because those were the only things I actually understood well.
Also, again in my personal experience, it's much better to declare them just before you use them for future maintenance, having 4 or 5 declarations at the same time is something I do so rarely now. If you start faffing around laying them out, you're going to be tempted to put them all together.
Also with Javascript's function-scoped variables and the fact it will let you declare them more than once in a given scope (the second and subsequent decleration being taken as either a NOOP or a basic assignemtn (if there is assignment with the decleration), declaring variables at the point of first use can be dangerous as it is easy, especially if you are messing around in global scope (which is to be availaded generally, but can't always be), to create hard to diagnose bugs due to variable values being clobbered because the same name is used elsewhere.
I find it much more important to put in javadoc style comments to make the code understandable for other users rather than lining things up nicely.
If you're using an IDE you can view all of the variables and methods sorted however you like anyway with a class or outline view.
x == 1
? alert(x)
:(
alert('Error'),
x = 1,
alert('All Fixed!')
);Alerting 'All Fixed' and afterwards 'Error' seems like it'd be just as valid an interpretation of the code.
Edited to add: Actually, I think the comma operator does do that, so no bad. Though the code is still unsightly...
thing[conditional ? 'show' : 'hide']();
instead of:
thing[if conditional then 'show' else 'hide']()
My only gripe is with placement of commas in object literals and variable declarations which has become so common over the past three or four years.
var x = 1
, y = 2;
or var x = 1
,y = 2
or {
k: 1
,k2: 2
}
Are terribly unsightly, and do not in any way benefit the maintainer. It's just now you have to worry about the first line rather than the last one.For this reason, CoffeeScript provides the existential operator, which asks: does this variable exist? (Is it not null or undefined?) You can use it in the same places you would use "||", as well as some others:
# In place of `||`
result = answer ? default
# Does `object.value` exist?
if object.value?
# Lazy initialize the speed of the car:
car.speed ?= 60
# Give me the value of `length`, only if `object` exists.
length = jsonResponse.list.object?.lengthI'm not sure how many times this will have to be clarified, but the JS code on the CoffeeScript site are not examples, but the compiled JS. Even so, it's in many cases prettier than Oscar Godson's "eloquent" JS, IMHO.
So Javascript can be formatted to look kind of like CoffeeScript?
While I agree that Javascript is often misunderstood, to me his formatting of Javascript is making things much worse, in terms of maintenance (yeah...let's all line up equal signs...).
I personally DO NOT write CoffeeScript but I think the difference between CoffeeScript and Javascript goes beyond formatting. Writing CoffeeScript seems to me to be a syntactic improvement and eliminates some of the need to really learn Javascript (which I think takes quite a bit of effort)
He also completely misses the point about javascript's global-by-default variables and how easy it is to introduce them with typos, properly building classes, etc.
Using strict mode will avoid these kinds of mistakes.
But feel free to correct me if I'm wrong, but I doubt vanity is the primary reason people are using CS. My number one favorite feature is the list comprehension, and if it didn't have that, I might not be bothering with it.
The ternery operator has some readability problems, but it is an expression, not a statement. Turning statements into expressions is powerful, which is why CoffeeScript's if/then compiles into an expression. Too bad this wan't mentioned.
Likewise, the comma operator allows JavaScript programmers to use a block of consecutive statements as an expression, something the author points out when explaining how to use ternaries in place of if statements.
The notion that you can use a sequence of statements as an expression without having to resort to a "let" form (function () { ...; ...; ...; return foo; })() is another underused construct that can make for improved JavaScript.
Put the two ideas together, and yes you can express a multi-line if statement with shorter keywords, but more importantly you have an if expression without having to wrap it in a function and encumber it with return statements.
I'm sorry that idea wasn't given more attention. If you aren't using JavaScript directly, this pattern may be useful in a more functional, expression-oriented style of programming.
Not only is it more readable, it's more correct in the example given.
"if" is a statement. The ternary operator (?:) is an expression, that returns a value. The semantics are different, and using the ternary operator "merely" to replace an if statement, especially where no assignment of the ternary operator's return value is made, is a code smell.
The ternary operator is great if you're replacing a simple "if something assignment to X else other assignment to X." Any other use is questionable.
They work only in a monospaced font. I code in a proportional font, and none of the column alignment tricks will look right when I view the code.
Even if you like to code in a monospaced font, there's no reason to write code that becomes hard to read in a proportional font. It's very easy to write code that is perfectly readable in both monospaced and proportional fonts: you merely forgo these column alignment tricks.
Also, this style of code is a pain to maintain. If you add a variable with a longer name, you have to go up and down adding spaces in front of all the other = signs.
And I find it hard to read even in a monospaced font, e.g.
var i = 123
, j = 456
, k = 789
, aMuchLongerVariableName = 'foo';
Matching up the initializer values for i, j, and k means my eye has to scan way across a sea of whitespace and hopefully remember which line is which when I get there.Finally, the example with the escaped newlines all in a vertical column does not do at all what he expects! All those extra spaces become part of the string.
That's really your problem. If you think people will stop aligning things in their code because you are in the tiny minority that use proportional fonts, you're crazy.
> Matching up the initializer values for i, j, and k means my eye has to scan way across a sea of whitespace and hopefully remember which line is which when I get there.
Nobody's actually going to do what you just showed in real code. Instead, it would look like this:
var i = 123,
j = 456,
k = 789,
aMuchLongerVariableName = 'foo';
And still look quite good.> Finally, the example with the escaped newlines all in a vertical column does not do at all what he expects! All those extra spaces become part of the string.
Absolutely, but if you are putting HTML in that string (a while other discussion there) 99% of the time the whitespace will not matter. And for all the other cases, this is no different from multiline string behavior in Python and other languages.
The first point he raises is a valid argument; CoffeeScript's intro does uglify the JS a little to make its point. But it doesn't take much.
> ...merely inaccurate due to a lack of knowledge on JavaScript...
Kinda think the opposite idea was how CoffeeScript came into being, amirite? It was written to make JavaScript look AND act better. It wasn't developed by people that had no idea how to use JavaScript, and it certainly isn't used exclusively by people who don't know how to use JavaScript, too.
Other than that CoffeeScript is pretty cool.
list.map((num) ->
num * 2
).map((doubled) ->
doubled + 5
)
... instead of: list.map(function(num) {
return num * 2;
}).map(function(doubled) {
return doubled + 5;
});
It's not a huge difference. But, by the same token, it's certainly not a reason to avoid the use of anonymous functions.Another thing CoffeeScript handles unelegantly is two anonymous functions (very common when a function needs an onsuccess and an onerror):
api.get(
->
# Success here.
->
# Failure here.
)
The benefit of curlies is you get a consistent experience and don't have to go to Stackoverflow to find out when you need commas or parentheses and when you don't.He should have gone into more detail to explain how ||'s work, how you can have more than just 1 of them, the order in which they are evaluated if you do have more than 1, etc.