In block comments, CoffeeScript converts #
into JavaDoc-style *
. Hence,
Js2coffee will transform *
inside block comments into the more
CoffeeScript-like #
.
// Input:
a();
/*
* hello
*/
b();
# Output:
a()
###
# hello
###
b()
Certain keywords in CoffeeScript are not allowed. For instance, on
is
actually an alias for true
.
The CoffeeScript code on = 2
will produce errors. As such, Js2coffee will
throw an error if any of the CoffeeScript reserved keywords are used.
If compatibility mode ins on (--compat
), it will be escaped in backticks so
to prevent any side effects.
// Input:
on = 2
# Output:
`on = 2`
The ==
operator has no equivalent in CoffeeScript. If you use ==
in
CoffeeScript, it will be compiled into ===
.
As such, Js2coffee will throw a warning when ==
is used, but it will behave
like ===
.
If compatibility mode ins on (--compat
), it will be escaped in backticks so
to prevent any side effects.
// Input:
if (a == b(c + 2)) { run(); }
# Output:
if `a == b(c + 2)`
run()
Named function expressions are not supported in CoffeeScript.
If compatibility mode is on (--compat
), they will be escaped into backticks.
// Input:
var x = function fn() {
return fn;
}
# Output:
x = (`function fn() {
return fn;
}`)
Named function expressions are not supported in CoffeeScript.
If compatibility mode is off (--compat
), they will be treated like any
other function expression, but may behave unexpectedly. In this example,
the typeof
will return 'undefined'
in CoffeeScript instead of the
expected 'function'
.
// Input:
var x = function fn() {
return fn;
};
alert(typeof x())
# Output:
x = ->
fn
alert typeof x()
It's possible for undefined
to be redefined in JavaScript, eg, var undefined = 2
. While this is undesirable and never recommended, Js2coffee
will that using undefined
will use whatever undefined
is defined as.
This is only available if compatibility mode is on (--compat
).
// Input:
undefined
# Output:
`undefined`
Objects aren't usually braced unless necessary. This leads to ambiguous
constructions such as one:1, two:2
where both objects are meant to be
separated.
// Input:
a({ one: 1 }, { two: 2 })
# Output:
a { one: 1 }, two: 2
Named function declarations in JavaScript can appear at any point of the scope, and they will be available anywhere in the scope. CoffeeScript doesn't allow named function declarations, however.
To get around this, js2coffee takes function declarations and puts the on top of the scope.
This is an improvement over js2coffee 0.x that only looks in the same level of the function body, not recursing into deeper blocks.
// Input:
alert(name());
if (ok) {
a();
function name() {
return "John";
}
}
# Output:
name = ->
'John'
alert name()
if ok
a()
Having a return of an object without braces is ambiguous:
return a: 1 b: 2
CoffeeScript and CoffeeScriptRedux will both choke on this. Js2coffee will put braces around the object to make it work.
// Input:
function fn() {
if (x)
return { a: 1, b: 2 };
return true;
}
# Output:
fn = ->
if x
return {
a: 1
b: 2
}
true
Some libraries shield against the JavaScript flaw of undefined
being
possible to redefine by using a function wrapper that includes an
undefined
parameter.
CoffeeScript will throw an error when one of the functions have undefined
as its parameters (eg, (undefined) ->
). Js2coffee will simply strip it out
of the parameter list.
This is not accounted for in js2coffee 0.x.
// Input:
function fn (undefined) {
return true;
}
# Output:
fn = ->
true
CoffeeScript doesn't support if
blocks that don't have anything in it (as
of CoffeeScript v1.8.0). To work around this, Js2coffee inserts an empty
else
block along with it.
// Input:
if (condition) {}
# Output:
if condition
else
CoffeeScript doesn't allow loop constructs (while/loop/for) without any
body. To get around this, we use continue
in place of an empty body.
// Input:
while (a) {}
# Output:
while a
continue
Since CoffeeScript has no for
loops, they
have to be converted to while
loops. If
continue
happens inside the loop, it needs
to re-run the update expression just before
it.
// Input:
for (a; b; update++) {
if (x) continue;
d()
}
# Output:
a
while b
if x
update++
continue
d()
update++
CoffeeScript has no for
loop, so they are converted into while
loops.
// Input:
for (a;b;c) {
d();
}
# Output:
a
while b
d()
c
Expressions that begin with a space will be converted into RegExp(...)
to
avoid parse errors.
// Input:
/ /g
# Output:
RegExp ' ', 'g'
A RegExp literal starting with an equal sign is not allowed in CoffeeScript,
as it's ambiguous and clashes with the /=
operator.
// Input:
a(/=\s/)
# Output:
a RegExp('=\\s')
CoffeeScript doesn't support shadowing of outer variables (see coffee-script#712). js2coffee uses a terrible hack to make this work.
Previously, this is unsupported in js2coffee 0.x.
// Input:
var val = 2;
var fn = function () {
var val = 1;
return;
}
fn();
assert(val === 2);
# Output:
val = 2
fn = ->
`var val`
val = 1
return
fn()
assert val == 2
CoffeeScript supports the **
binary expression to calculate exponents.
// Input:
Math.pow(2, 8)
# Output:
2 ** 8
CoffeeScript allows function calls without parentheses, such as alert "Hello"
. With this, you can do strange constructions such as push new Sidebar $ "left"
(that is: push(new Sidebar($("#left")))
).
This is unreadable, however. Ruby has the same constructions, but Ruby styleguides often advocate not omitting parentheses unless the call expression is a statement.
Js2coffee takes the same convention into consideration.
// Input:
a(b(c(d())));
# Output:
a b(c(d()))
CoffeeScript allow prototypes as ::
, as in Array::join = ->
.
It also allows a::
without anything on the right side (as is the case
of the 2nd line), but CoffeeScriptRedux doesn't.
// Input:
a.prototype.b = 1
a.prototype = {}
# Output:
a::b = 1
a.prototype = {}
CoffeeScript allows this
as @
. In fact, js2coffee compiles this.x
into @x
.
Using a standalone @
was once allowed in CoffeeScript, but was removed in
future versions. Hence, standalone JavaScript this
expressions compile into
the same thing, this
.
// Input:
var a = this;
# Output:
a = this
This is previously broken in js2coffee 0.x since Narcissus didn't handle this case properly.
// Input:
a ? b : c ? d : e
# Output:
if a then b else if c then d else e
When using this
on the left-side of a .
, it gets converted into
CoffeeScript-style @
prefixes.
When this
is used on its own (such as in the case of the 2nd this
in the
example), it is left alone as using a standalone @
is discouraged.
// Input:
this.run(this);
# Output:
@run this
CoffeeScript doesn't support the void
operator. However, doing void (anything)
will always be identical to void 0
, and CoffeeScript compiles
the undefined
keyword into void 0
. Hence, all void XXX
expressions will
compile into undefined
.
That is: void 100 === void 0 === undefined
.
// Input:
void 0
# Output:
undefined
Strings are defaulted to single quotes to prevent interpolation.
// Input:
"#{a}"
# Output:
'#{a}'
CoffeeScript doesn't support adding when
clauses that are empty, as you
probably would in JavaScript. Js2coffee will consolidate empty case
clauses
together to make things more readable.
// Input:
switch (a) {
case one:
case two:
b();
break;
default:
c();
}
# Output:
switch a
when one, two
b()
else
c()
CoffeeScript doesn't support adding when
clauses that are empty, as you
probably would in JavaScript. When an empty case
is used just before
default:
, it is effectively useless and is stripped away.
// Input:
switch (a) {
case one:
b();
break;
case two:
default:
c();
}
# Output:
switch a
when one
b()
else
c()
Doing var a
in JavaScript will declare that the current
function scope has a variable a
in it, preventing things like alert(a)
from getting the global a
.
CoffeeScript has no such construct as var
. However, since JavaScript
initializes all variables as undefined
, doing an assignment to undefined
(a = undefined
) will yield the same result.
// Input:
var a;
# Output:
a = undefined