CoffeeScriptの関数は明示的にreturnするべき

※ 追記 2014.1.13
ご指摘いただきました。そのとおりなので、心を入れ替えて勉強します。
「CoffeeScriptの関数は明示的にreturnしてはいけない理由」を探す暇あったら他にやるべきことあるのでは? – mizchi’s blog


お仕事ではJavaScriptではなくCoffeeScriptをたくさん書いている@kadoppeです、こんにちは。

最近いろいろと考えた結果、「CoffeeScriptで書くすべての関数は明示的にreturnするべき」、という結論に落ち着きました。その経緯や理由について書いてみます。

暗黙的なreturn

CoffeeScriptで、明示的にreturnされていない関数を定義した場合、関数内の最後の式を評価した結果の値が、戻り値として返される仕様になっています。

例えば、引数を足し合わせる関数をCoffeeScriptで以下のように定義します。

上のコードは、以下のようにJavaScriptにコンパイルされます。

引数を足しあわせた結果の値がreturnされています。いちいちreturnを書かなくても自動的にreturnしてくれるところは、CoffeeScriptの便利な機能のひとつです。

問題: 無駄なJavaScriptコードが出力される

例えば、以下のような関数をCoffeeScriptで定義します。

配列の各要素をループでログ出力するだけの単純な関数です。このコードをJavaScriptにコンパイルすると以下のようになります。

少し複雑なコードが出力されました。コードをよく見てみると、_resultsという配列が定義され、毎ループのコードの評価結果が配列に毎回pushされています。なんだか無駄なコードに見えますが、どうしてこんなコードが出力されるのでしょうか。

この関数では明示的にreturnしていません。なので、CoffeeScriptは最後に評価された値を関数の戻り値として暗黙的にreturnしようとします。

関数内の最後の式はfor inです。CoffeeScriptのfor inは値を返す仕様になっています。値を求められた場合にのみ、for in内部の最後の式の評価結果を、配列にまとめて返してくれます。

まとめると、上のコードでは、

  • 関数内の最後の式がfor inであるため、関数の戻り値としてfor inを評価した結果の値を返そうとする
  • for inの値を用意するために配列が定義され、毎ループの評価結果がpushされている

ということが起こっています。本当にfor inの評価結果が欲しいのであればこれでいいのですが、そうでない場合は無駄なJavaScriptコードを実行してしまっていることになります。

「たかがpushくらいいいやん」と思われるかもしれません。ですが、この関数が繰り返し何度も呼び出されるような場合、パフォーマンス上の懸念があります。また、例えば以下のようにfor inがネストしていた場合、事態は深刻になります。

このコードをJavaScriptにコンパイルすると以下のようになります。

ネストが増えた分、配列の数やpushされる回数が増えています。また、毎ループで無名関数が生成されるようなコードになっており、さらにパフォーマンス上の懸念が高まります。

解決: 明示的にreturnする

こんな無駄なJavaScriptコードが生成されないようにするためには、関数の最後で明示的にreturnすればよいです。

for inがネストしていない関数の最後で明示的にreturnすると、

以下のようなJavaScriptに変換されます。無駄なpushがなくなりました。

ネストしていても一緒。明示的にreturnすると、

以下のようなJavaScriptに変換されます。pushはもちろん、無名関数も生成されなくなります。

すべての関数で明示的にreturnするべき?

上記のような問題が発生する関数はまれかもしれません。なので、問題が発生する関数でのみ明示的にreturnする方針もありです。

でも僕の考えとしては、「CoffeeScriptで書くすべての関数は明示的にreturnすべき」です。

問題が発生する関数でのみ明示的にreturnする方針の場合、怖いのはreturn漏れです。気づかないうちに無駄なJavaScriptコードが実行されていた、ということが起こりえます。

また、複数人で開発を進めている場合、たとえ自分自身で気をつけて漏れがないようにできたとしても、必ずどこかで漏れが発生するような気がします。

だとしたら、コーディング規約レベルで「すべての関数で明示的にreturnする」という一文を明文化するべきです。そのルールに則ってコードを書いた方が、事故が発生する可能性も減って、より、品質の高いコードが書けるようになるのではないでしょうか。

たかがreturnですが、コンパイルされたJavaScriptに大きな違いをもたらします。違った考えの人がいればぜひ教えて欲しいです。

これからはコンパイルされた結果のJavaScriptにもしっかり目を向けていこう、と心に決めたkadoppeでした。
それでは!