じゃあ結局Javascriptの関数定義ってどっちで書けばいいんだ?
Javascriptの本を読んでいると、関数の定義にはfunction式と文があり、それぞれ性質の違いがあるようなことが書かれている。
//こうやって書いたり function tmp() { //何か処理 } //こうしてみたり var tmp = function () { //何か処理 };
前者後者の違いは主に、前者では巻き上げが行われ、後者は行われないというもの。
(function () { //tmpはこの地点で未宣言なのになぜか呼び出せてしまう。 tmp(); function tmp() { window.alert("unko!"); } }());
つまり前者の方がルーズなコードになりがち、ということだ。本に書かれているのは大体ここまで。わざわざルーズなコードを書く必要はないワケで、それじゃあいつも後者 var ... のスタイルを使えばいいのか? ってのは当然の疑問だ。
私の経験則に従えば、基本的にはその方針、つまりいつでも var... を使うという方針で問題ない。その実 var ... の方針で書けないソースというのは非常に珍しく、どうしても書けないのは(関数間で相互参照があるなど)、そのソース自体に何か問題がある。 Javascript を勉強したこともない人間(大抵はJavaプログラマーだ)が「なんとなく」書いてみて、「オレ Javascript 知らねえから」と言い訳を垂れ流しているようなものだ。それは言語の問題ではない。つまりクソだ。
それでも実のところ前者を使うべきケース無いということではない。以下の二つがそれに該当する。
- グローバル関数を作成する場合
- 変数代入前に宣言が確立されているべきケース。
まっとうなソースを書いているのであれあば、グローバル関数の作成は共通関数の作成など極一部に限られるはずだ*1。もちろん共通関数とて var ... で書けない事は無い。が、わざわざ var で宣言したオブジェクトに「代入」する必要もない。
他方、変数代入前に宣言が確立されているべきケースとは、具体的に再起で発生する。以下の例は Javascript としては適当に動作するが、厳密には怪しい部分が隠されている。
var tmp = function (str) { str += " unko!"; if (str.length < 20) { //この function が宣言されたタイミングでは tmp はまだ undefined tmp(str); } else { window.alert(str); return; } };
var ... の記法はあくまで無名関数の変数への代入だ。この無名関数は宣言時に undefined である tmpの呼び出しを行っていることになり、厳密にはエラーとなる。もっとも実際に tmp が実行される際には既に代入はすんでいるため、動作はするだろうが、ソース内にエラーがあると判りながら放置するのは気持ちが悪いものだ。再起関数の宣言と共に実行&後から再度呼び出すようなトリッキーなケースではダイレクトにエラーすら発生する。おめでとう、言い訳無しだ!
var tmp = (function (str) { str = str || ""; str += " unko!"; if (str.length < 20) { //ここでコケる return tmp(str); } else { window.alert(str); return tmp; } }());
従って以下のようにする。
function tmp(str) { str += " unko!"; if (str.length < 20) { tmp(str); } else { window.alert(str); return; } }
もしくはこうする。
var tmp = function tmp(str) { str += " unko!"; if (str.length < 20) { tmp(str); } else { window.alert(str); return; } };
この2ケース以外では常に var... を使っていればいい、というのが結論となるが…、皆様のご意見を伺いたい。
*1:グローバル関数をひとつでも作成するのが「まっとう」なのかという議論はひとまず置いておく