Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/foxbunny/cstricks
CoffeeScript Tricks
https://github.com/foxbunny/cstricks
Last synced: 28 days ago
JSON representation
CoffeeScript Tricks
- Host: GitHub
- URL: https://github.com/foxbunny/cstricks
- Owner: foxbunny
- Created: 2013-09-05T10:47:51.000Z (over 11 years ago)
- Default Branch: master
- Last Pushed: 2013-09-24T12:22:03.000Z (about 11 years ago)
- Last Synced: 2023-03-10T22:13:37.838Z (almost 2 years ago)
- Size: 152 KB
- Stars: 1
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.mkd
Awesome Lists containing this project
README
# CoffeeScript Tricks
These are some of the tricks I use from time to time to improve the code
readability and/or speed things up.## Contents
+ [Immediately-invoked function expression](#iife)
+ [Iterating arrays in reverse](#reverse)
+ [Getting the last item in an array](#lastitem)
+ [Return or throw](#retthrow)
+ [Deluxe list comprehensions](#deluxecomp)
+ [Recursive function as object property](#recursiveprop)
+ [UMD wrapper](#umdwrapper)
+ [Clean up JavaScript by returning](#cleanreturn)## Immediately-invoked function expression (IIFE)
This might not be such a 'big' trick, but here's the syntax:
((args...) ->
) args...
Yeah, nothing too spectacular here. Moving on.
## Iterating arrays in reverse
This one might also be obvious to most, but here it is anyway:
a = [1, 2, 3, 4]
for i in a by -1
console.log i## Getting the last item in an array
Most people would think:
a[a.length - 1]
But there's a slightly better way:
a[-1..][0]
And, no, `a[-1]` doesn't work. And, yes, you can still use `a.pop()` if you
don't care about what happens to `a`.This is one of those patterns that look so obvious when written in
CoffeeScript, and yet it compiles into something quite delicious and hackish.a = () ->
# calculate x
x or throw new Error 'OMFG!'You just have to see what it looks like when compiled:
var a;
a = function() {
return x || (function() {
throw new Error('OMFG!');
})();
};Let's say we have an array of objects, and we want to transform that object in
some way and create a new array or simply overwrite the old one.Let's say the array looks like this:
a = [
{a: 2, b: 4}
{a: 3, b: 5}
{a: 12, b: 0}
]Now let's add a `c` key that will be sum of keys `a` and `b`:
a = (
((o) ->
a: o.a
b: o.b
c: o.a + o.b
) o for o in a
)The `a = (expression for o in a)` is a classic list comprehension. For the
`expression` we use an IIFE which, extracted, looks like this:(o) ->
a: o.a
b: o.b
c: o.a + o.b
) oYou can exclude the outer brackets of the list comprehensions provided that you
do not change the relative position in code of the actual comprehension. The
code below compiles to identical JavaScript as the code above:a = # <-- look, mommy, no bracket!
((o) ->
a: o.a
b: o.b
c: o.a + o.b
) o for o in aThe reason CoffeeScript compiler works like this is beyond me, so don't ask.
The above form is a bit confusing, though, so let's just use brackets when
assigning to variables.You're also guessing correctly that we can also use the deluxe version of
`when` in the comprehensions:a = # <-- look, mommy, no bracket!
((o) ->
a: o.a
b: o.b
c: o.a + o.b
) o for o in a when ((o) ->
o.a > o.b
) oWhen using the deluxe list comprehensions as a return value, you don't need to
include the outer brackets.c = () ->
((o) ->
a: o.a
b: o.b
c: o.a + o.b
) o for o in a # <-- look, mommy, no brackets!When using as return value, I think it's ok to just omit the brackets. It
doesn't make any difference at all.## Recursive function as object property
You can use a recursive function as an object property. The problem is usually
how to call this function from within itself when it's being assigned as a
property. Take a look at this:a =
b: () ->How do you call `a.b` from within itself? Well, you can simply call it as
`@b()`a =
b: (i, n=0) ->
return n if not i
@b(i - 1, n + i)What if we want to use b stand-alone? For example, we want to do something
like:b = a.b
b 12In this case, we need `b` to refer to itsef using the reference to `a`:
a =
b: (i, n=0) ->
return n if not i
a.b(i - 1, n + i)Alternatively, you can also do this:
a =
b: b = (i, n=0) ->
return n if not i
b(i - 1, n + i) # <-- `b` refers to `b` variable in same scope as `a`This makes `b` both `a`'s property, and a stand-alone `b` variable within the
same scope where `a` is defined (this works in JavaScript as well).If you want to create an
[UMD](https://github.com/umdjs/umd/#umd-universal-module-definition) wrapper
for your AMD module, there are many ways to do it, including using IIFE, which
is a [recommendation by James
Burke](https://github.com/umdjs/umd/blob/master/commonjsAdapter.js). Here,
we'll take a look at an alternative syntax which works well in CoffeeScript
when using a CommonJS-style module that calls `require`.The UMD wrapper below supports browsers without AMD loaders, as well as NodeJS
modules.define = ((root) ->
if typeof root.define is 'function' and root.define.amd
root.define # Return RequireJS's version of define
else
if typeof module is 'object' and module.exports
(factory) -> module.exports = factory(root.require)
else
require = (dep) ->
(() ->
switch dep
when 'underscore' then root._
else null
)() or throw new Error "Unmet dependency #{dep}"
(factory) -> root.myModule = factory(require)
) thisdefine (require) ->
_ = require 'underscore'The above wrapper may seem huge, but it actually takes care of dependencies and
also traps any missing dependencies when working with non-AMD browser
situation.If the module has no dependencies, it can be simplified:
define = ((root) ->
if typeof root.define is 'function' and root.define.amd
root.define # Return RequireJS's version of define
else
if typeof module is 'object' and module.exports
(factory) -> module.exports = factory()
else
(factory) -> root.myModule = factory()
) thisdefine () ->
Further, if you do not need support for NodeJS, it's even simpler:
define = ((root) ->
if typeof root.define is 'function' and root.define.amd
root.define # Return RequireJS's version of define
else
(factory) -> root.myModule = factory()
) thisdefine () ->
Last but not least, if you do need dependencies, but don't need NodeJS:
define = ((root) ->
if typeof root.define is 'function' and root.define.amd
root.define # Return RequireJS's version of define
else
require = (dep) ->
(() ->
switch dep
when 'underscore' then root._
else null
)() or throw new Error "Unmet dependency #{dep}"
(factory) -> root.myModule = factory(require)
) thisdefine (require) ->
_ = require 'underscore'## Cleaning up JavaScript by returning
If you are like me, you are probably used to coding CoffeeScript blind-folded
and you rarely look at the output code (unless something breaks, that is, and
maybe not even then). Sometimes, though, code that _works_ might not be as nice
in JavaScript as it seems in CoffeeScript. In some cases this is because in
CoffeeScript, everything is an expression, and the last expression evaluated
will be returned from a function.Consider this example:
b = []
a = (x) ->
b.push i + 1 for i in xThis function returns the result of the list comprehension, because that is the
last expression in the function. It compiles to this handful:
var a, b;b = [];
a = function(x) {
var i, _i, _len, _results;
_results = [];
for (_i = 0, _len = x.length; _i < _len; _i++) {
i = x[_i];
_results.push(b.push(i + 1));
}
return _results;
};
var a;But clearly, in this case, we aren't really interested in the return value
which generated JavaScript frantically gathers and returns. In this instance,
we should simply `return` at the end of the function and let CoffeeScript know
that we don't expect any meaningful return value.a = (x) ->
b.push i + 1 for i in x
returnThis makes the JavaScript a lot slimmer, and depending on what you are doing,
much faster, too.a = function(x) {
var i, _i, _len;
for (_i = 0, _len = x.length; _i < _len; _i++) {
i = x[_i];
b.push(i + 1);
}
};## License
CoffeeScript Tricks by Branko Vukelic is licensed under a Creative Commons
Attribution 3.0 Unported License.
Based on a work at https://github.com/foxbunny/cstricks.