イディオム
すべての言語にはイディオムやプラクティスのセットがあり、CoffeeScriptも例外ではありません。この節では、これらのルールをについて学び、言語の実用的な感覚を得ることができるように、比較のためにいくつかのJavaScriptが同時に表示したいと思います。
Each
JavaScriptでは、新しく追加された forEach()
か、一般的な反復処理を使う事が出来ます。ECMAScript 5で導入されたJavaScriptの最新の機能を使用したい場合、古いブラウザ向けにはshimの導入をお勧めします。
for (var i=0; i < array.length; i++)
myFunction(array[i]);
array.forEach(function(item, i){
myFunction(item)
});
forEach()
構文ははるかに簡潔で読みやすいですが、一方でコールバック関数が反復ごとに呼ばれているため、JavaScriptで書かれるループのよりも遅くなってしまいます。以下の例を見てみましょう。
myFunction(item) for item in array
確かに読みやすく簡潔な構文で、素晴らしいことに裏側で for
ループにコンパイルしてくれます。言い換えれば、CoffeeScriptは forEach()
とほぼ同じ表現力を提供し、かつ速度や古いブラウザへの互換を気にせずによいということです。
Map
forEach()
と同様に、クラシックな for
ループに加えて、ES5では map()
という、ネイティブなマップ機能が導入されます。しかし forEach()
と同様にパフォーマンスの点では注意が必要です。
var result = []
for (var i=0; i < array.length; i++)
result.push(array[i].name)
var result = array.map(function(item, i){
return item.name;
});
構文の節で説明したように、CoffeeScriptの内包表記は map()
と同様の機能を果たしています。配列を返すことを明示するために、内包表記は必ず括弧で囲わなくてはいけません。
result = (item.name for item in array)
Select
ES5には filter()
が導入され、配列をフィルタリングをすることができます。
var result = []
for (var i=0; i < array.length; i++)
if (array[i].name == "test")
result.push(array[i])
result = array.filter(function(item, i){
return item.name == "test"
});
CoffeeScriptの構文では、 when
キーワードを用いて要素を比較する事が出来ます。実際に実行する関数はスコープ漏れや変数のコンフリクトを避けるために匿名関数(Anonymous Function)が用いられます。
result = (item for item in array when item.name is "test")
括弧を忘れてしまうと 結果は配列の最後の項目になるので忘れないように注意しましょう。CoffeeScriptの内包表現は非常に柔軟なので、以下のような場合も対応できます。
passed = []
failed = []
(if score > 60 then passed else failed).push score for score in [49, 58, 76, 82, 88, 90]
# Or
passed = (score for score in scores when score > 60)
If comprehensions get too long, you can split them onto multiple lines.
passed = []
failed = []
for score in [49, 58, 76, 82, 88, 90]
(if score > 60 then passed else failed).push score
Includes
値が配列内にあるかどうかを確認する場合、通常(もちろんこの"通常"にはIEは含まれていません)は indexOf()
を用いる事でチェックする事が出来ます。
var included = (array.indexOf("test") != -1)
CoffeeScript has a neat alternative to this which Pythonists may recognize, namely in
.
included = "test" in array
どのようにコンパイルされているかというと、CoffeeScriptは Array.prototype.indexOf()
を使って配列をチェックしています。しかし、この場合文字列に対しては使う事が出来ません。そのため文字列の場合は indexOf()
を用いましょう
included = "a long test string".indexOf("test") isnt -1
より良い例は、-1
の比較をビット演算子をつかって代替する方法です。
string = "a long test string"
included = !!~ string.indexOf "test"
Property iteration
To iterate over a bunch of properties in JavaScript, you'd use the in
operator, for example:
var object = {one: 1, two: 2}
for(var key in object) alert(key + " = " + object[key])
However, as you've seen in the previous section, CoffeeScript has already reserved in
for use with arrays. Instead, the operator has been renamed of
, and can be used like thus:
object = {one: 1, two: 2}
alert("#{key} = #{value}") for key, value of object
As you can see, you can specify variables for both the property name, and its value; rather convenient.
Min/Max
このテクニックは、CoffeeScriptに固有のものではありませんが、通常 Math.max
と Math.min
は複数の引数を取るのですが、...
を最後に付けることで、配列を引数として処理する事ができます。
Math.max [14, 35, -7, 46, 98]... # 98
Math.min [14, 35, -7, 46, 98]... # -7
It's worth noting that this trick will fail with really large arrays as browsers have a limitation on the amount of arguments you can pass to functions.
Multiple arguments
In the Math.max
example above, we're using ...
to de-structure the array and passing it as multiple arguments to max
. Behind the scenes, CoffeeScript is converting the function call to use apply()
, ensuring the array is passed as multiple arguments to max
. We can use this feature in other ways too, such as proxying function calls:
Log =
log: ->
console?.log(arguments...)
Or you can alter the arguments before they're passed onwards:
Log =
logPrefix: "(App)"
log: (args...) ->
args.unshift(@logPrefix) if @logPrefix
console?.log(args...)
Bear in mind though, that CoffeeScript will automatically set the function invocation context to the object the function is being invoked on. In the example above, that would be console
. If you want to set the context specifically, then you'll need to call apply()
manually.
And/or
CoffeeScript style guides indicates that or
is preferred over ||
, and and
is preferred over &&
. I can see why, as the former is somewhat more readable. Nevertheless, the two styles have identical results.
This preference over more English style code also applies to using is
over ==
and isnt
over !=
.
string = "migrating coconuts"
string == string # true
string is string # true
CoffeeScriptでサポートされているとても便利な構文として、 or equals
があり、Rubyの ||=
に相当します。
hash or= {}
<<<<<<< HEAD
ハッシュが false
だと評価された場合、それは空のオブジェクトだということになります。[]
や ""
さらには 0
もfalse
として評価されることに気をつけなくてはいけません。もしそれを意図してない場合は、hash
が undefined
または null
場合にのみアクティブになるCoffeeScriptの存在確認演算子を使いましょう。
If hash evaluates to false
, then it's set to an empty object. It's important to note here that this expression also recognizes 0
, ""
and null
as false. If that isn't your intention, you'll need to use CoffeeScript's existential operator, which only gets activated if hash
is undefined
or null
:
hash ?= {}
Destructuring assignments
深くネストされたオブジェクトや配列のプロパティも以下のようにして簡単にアクセスすることが出来ます。
someObject = { a: 'value for a', b: 'value for b' }
{ a, b } = someObject
console.log "a is '#{a}', b is '#{b}'"
<<<<<<< HEAD
External Libraries
=======This is especially useful in Node applications when requiring modules:
{join, resolve} = require('path')
join('/Users', 'Alex')
External libraries
>>>>>>> upstream/master最終的には結局JavaScriptにコンパイルされるので、外部ライブラリを使用する場合もCoffeeScriptのライブラリを呼び出すのと全く同じです。 jQueryをCoffeeScriptで使うときは、jQueryのAPIのコールバックのパターンをよりエレガントに書く事が出来ます。
# Use local alias
$ = jQuery
$ ->
# DOMContentLoaded
$(".el").click ->
alert("Clicked!")
CoffeeScriptからコンパイルされたのコードはすべて、無名関数内にラップされているので、jQuery
のためのエイリアスである$
をローカルにセットして使うことが出来ます。
Private variables
The do
keyword in CoffeeScript lets us execute functions immediately, a great way of encapsulating scope & protecting variables. In the example below, we're defining a variable classToType
in the context of an anonymous function which's immediately called by do
. That anonymous function returns a second anonymous function, which will be ultimate value of type
. Since classToType
is defined in a context that no reference is kept to, it can't be accessed outside that scope.
# Execute function immediately
type = do ->
classToType = {}
for name in "Boolean Number String Function Array Date RegExp Undefined Null".split(" ")
classToType["[object " + name + "]"] = name.toLowerCase()
# Return a function
(obj) ->
strType = Object::toString.call(obj)
classToType[strType] or "object"
In other words, classToType
is completely private, and can never again be referenced outside the executing anonymous function. This pattern is a great way of encapsulating scope and hiding variables.