Hatena::ブログ(Diary)

三等兵

2011-08-24

知ってて当然?初級者のためのJavaScriptで使う即時関数(function(){...})()の全て

(function(){...})()は、

(function($){
  $.hoge = function() { };
})(jQuery)

みたいに使われていたりするコード。GreasemonkeyとかjQueryのプラグインとか、あれこれ見かけることがあると思います。

この話題はいくつかWebでも取り上げられていますが何がどうなってんのかちょっと難しいですね。しかし、誰でも理解できるレベルではあります。というのも、こういう種の難しさは体系的な知識が備わっているか否かということなのです。


でも、この知識を体系化する作業って結構しんどくて、難しくて、まーハゲるほど悩むこともあるかもしれない。それはきっと、とても毛根に悪いかもしれない。スカルプDも真っ青の状況になるかもしれない。それは、悲しいことなのだと思う・・・っ!

毛根にはこれからもがんばってほしい!いつだって頭を温かいまなざしで見守ってて欲しい!そうだろ!だって、だって毛根だもん!毛根が頭から離れたら何になるのさ!井手らっきょになるのさ!?

いやだいやだ、ニコラス・ケイジやブルース・ウィリスならまだしも井出らっきょだけはいやだいやだ。やめておくれよハゲの神様。井手らっきょだけは、井手らっきょだけは・・・・・・ちょっといいかも(ポッ


そんな切ない即時関数の話。じゃなくて。普通の即時関数とそれに関係する言語仕様の話。


(function(){...})()の名前

その前に名前の話。

今まで(function(){...})()というコードには名前が定まっていませんでした。(個人で好きなように表現していた→”即実行関数”や”使い捨て関数”や"自己実行関数"などいろいろ)

個人的には(function(){...})()に対して名前を統一すべきだろうなあと思っていたところ、JavaScriptパターン ―優れたアプリケーションのための作法 で「即時関数」と名付けされているようです。

権威のある書籍に記述されているのであれば認知されやすいし、言葉としても分かりやすいのでこれからは即時関数という名が広まると良いですね。共通のイメージが持てれば会話にも出し易いし、検索もできますから。

タイトルや冒頭で既に使っていますが、以降から(function(){})()のことを即時関数とします。


※文中の用語について補足していただきました。ありがとうございます。
JavaScriptの関数(ラムダ関数とかクロージャーとか) - hogehoge @teramako


即時関数(function(){...})()ができること

即時関数は関数定義と関数呼び出しをまとめて行うことができるコード。

それを理解するためにまず普通の関数をおさらい。

function hoge() {
  alert(1);
}

これが関数定義。(正確には関数定義の関数宣言にあたる)


当然ですが関数は呼び出さないと何もおきません。呼び出すとは言い方を変えると「実行する」ってことです。じゃあ実行します。えいや。

function hoge() {
  alert(1);
}

hoge(); // 1

これが関数の呼び出し。定義した関数名の後ろに関数呼び出しの( )をつけます。さもありなん、みなまで言うなとパンチの構えはやめていただきたい。真剣なのです。

真剣に井手らっきょの進退についうぼげはぁあああ三○))゜д゜((○三

・・・えーfunction hoge( ) {...}が関数定義、hoge( )が関数の呼び出しにあたることを意識したうえで次。


関数は基本的に以上の段取りで利用するのですが、即時関数だとこれらをまとめて行なうことができます。

function hoge() {
  alert(1);
}

hoge(); // 1

// 上記の関数定義と関数呼び出しをまとめて書くと↓

(function hoge() {
  alert(1);
})();

見た目がなんかカッコの化けものになっちゃいましたが、それについては後述。

そういうわけで、即時関数とは、

  • 関数定義
  • 関数呼び出し

の2つをまとめて行なうことができるものです。


それで、このように1回実行するだけのケースだと、

(function() { // ←hogeっていう関数名は無くてもいい
  alert('教頭の頭ははっちゃけている');
})();

hogeという関数名をのぞいて無名関数(匿名関数とも言われている)で呼び出すのが一般的。多くの人が見たことあるのはこちらの無名関数でしょう。

再帰的に扱うケースやデバッグといった目的があれば名前をつけたりしますが、基本的に1回ぽっきりの野郎なんざぁ名前なんてどうでもいんだよふははは!ということで無名です。

「JSerの人たちは冷酷だよ。いつだって私たちを使い捨てなのさ。一回ポッキリで女ったらしあるいは男ったらしなんだよJserは。あううう。」

という嘆きを即時関数は利用されるたびに叫んでいることを我々は常に心にとどめておくことが大切です。どうでもいいです。


ともかく、このように即時関数は関数定義と関数の呼び出しをまとめて書ける非常に使い勝手の良いコード。

カッコのばけものな即時関数も関数定義と関数の呼び出しを意識してかんがえると、そりゃもう普通のことやってんじゃんあたりまえじゃんと思えます。

うんうん、でも、そもそも即時関数を使う必要があるんでしょうか。


定義と呼び出しがまとめて行えたらどういった効果(良いこと)があるのか?

即時関数は定義と呼び出しをまとめてできるってことはわかりました。

しかしながら、なんでそんなことする必要があるの?って疑問がわいてきます。先ほどの例でいえば、

function hoge() {
  alert(1);
}

hoge(); // 1

この形でも良いじゃないかと。

「確かに即時関数だとまとめて書けて良いかもしれない。でも別に定義と呼び出しを分けてやってもいいじゃないか!普通に書けばいいんだ!わざわざ即時関数にして私ってできるだろ?すごいだろ?結婚する?的なアピールやめろよ!あと結婚するよ!パンパカパーン!」

と思ったかもしれない。

即時関数男さんと結婚出来美さん、ちょっと即時関数男さんは生意気で高飛車なところもあるけれど、なにをやるにしても早いのが素敵なの。というかもう後がないの結婚しましょ!印鑑!印鑑!結婚届けどこおおおおおおお!

という感じですか?

まあともかく。いやこれはごもっともな話で、ではなぜ即時関数を使うのかと言えば、「グローバル変数の使用を抑えることができるから」という理由ですね。

って最初にこの理由を聞いて「ほうほう!そういうことかほうほう!」と納得できれば世の中みんな幸せなのですが、「グローバル変数の使用を抑えることができる」って抽象的な言葉でなんのこっちゃよくわからん。

そこで具体的にどう抑えられるかってのが次。理由は大きく2つあります。

1. グローバル変数に関数の割り当てがなされない

1つ目の理由は、即時関数だとグローバル変数に関数の割り当てがなされないことです。具体的な意味は、まず必要とする前提知識の確認のコード。

var hoge = 'ほげー';

alert(hoge === window.hoge); // true

なぜtrueになるか理解できれば大丈夫。グローバル変数は実際にはグローバルオブジェクトのプロパティです。

ブラウザの場合グローバルオブジェクトはWindowオブジェクトですね。Windowオブジェクトはwindowって自己参照のプロパティがあるんでwindowという小文字を使います。

なので、hoge === window.hogeはtrueということに。

ここでWindowオブジェクトやプロパティといった単語が理解できないと辛い。適当な入門書であれば言及しているはずなので確認してみてください。

そいで、これは関数でも一緒です。

function hoge() { alert('ほげー'); }

alert(hoge === window.hoge); // true

先ほどの普通の変数の例と一緒。

変数だけではなく関数も変数と同じようにグローバル変数に割り当てられています。つまり、グローバルの空間で関数定義すれば関数もWindowオブジェクトのプロパティになります。

関数ってどこか特別な感じがして、変数と区別して考えてしまうかもしれません。しかし、JSではどちらも同じように管理します。varやfunctionで定義しても、グローバルで宣言された変数や関数はWindowオブジェクトに属します。

変数だからとか関数だからって別々に区別しません。なので、

function hoge() { alert('ほげー'); }
alert(hoge); // function hoge() { alert('ほげー'); }

var hoge = 'ふげー';
alert(hoge); // ふげー

window.hoge = 'へげー';
alert(hoge) // へげー

代入するごとに値が変わっているのが分かります。この結果が成り立つのは当然ですね。

ここまでを踏まえて本題。


即時関数を使えば「グローバル変数に関数の割り当てがなされない」とは、今までを理解していれば次のコードですぐわかります。

(function hoge() { alert('ほげー'); })();

alert(hoge) // ReferenceError: hoge is not defined

ReferenceError: hoge is not definedは、「参照エラー: hogeは定義されていません」という意味。つまり、「hogeという関数はどっこにもありませんなー」と言われています。

関数にhogeって名前が付いているので「この関数もhogeというグローバル変数に割り当てられてるのかなー、window.hogeみたいになっているのかなー、とりあえず結婚しようかなー」と思ったら大間違い。しくしく。

即時関数は定義と呼び出しをまとめて行っています。だから、グローバル変数に設定する前に関数に定義された処理は役目を終えて消えました。なのでどっこにもねーよボケとエラーで怒られたんです。ぐすん。(関数宣言ではなく式として評価されたので割り当てもクソもないのです)

せっかくわざわざ名前つけてやってんのに意味がない。こんな放蕩薄情野郎に名前を付けたってしょうがないじゃん。

ってなことで、

/* 名前つけずに無名関数にする */
(function() { alert('ほげー'); })();

よく見る無名の形になる。

小見出しの「グローバル変数に関数の割り当てがなされない」という意味は、つまり、即時関数であればグローバル変数に割り当てられることなく関数が呼びだすことができるということです。

これが即時関数がグローバル変数の使用を抑えることができる1つ目の理由。


さて、これ以外にもう1つ抑えることができる理由がありまして。即時関数はそもそも関数なのですからスコープが提供されます。

2. 即時"関数"だから関数内部の変数はローカルに限定される

2つ目の理由は、即時関数を使えば内部で宣言した変数や関数はローカルに限定される、というものです。

これは前提知識としてJSのスコープを理解していなければなりません。すでにグローバル変数など単語を出しているのにいまさらですが。


スコープとは変数(関数も含む)の有効範囲です。有効範囲と言うとむつかしいイメージがしますが、変数や関数を参照できる場所(利用できる場所)を限定する仕組みのことですね。(プログラミングでは「変数の参照」とか「変数へのアクセス」とかって表現することがありますが、要は変数を使うってことです)

そしてJSのスコープは基本的にグローバルスコープとローカルスコープという2つのスコープに分けられ、ローカルスコープは関数で提供されています。


専門用語ばっかりじゃあれなんで、とりあえずスコープを簡単にレッツ体験。

function hoge() {
  var local = 'ローカル変数';
}

alert(local); // ReferenceError: local is not defined

関数の中で宣言したlocalという変数を、関数の外でalertしたらどっこにもないよーってエラーになりました。これは関数の中のlocalというローカルスコープを持つ変数が関数内に閉じ込められているためです。

このように関数内部で宣言された変数は関数の外部からは参照できません。(プログラミングではこのことを「変数がみえない」と言うことがある)

もちろん関数同士だって外部にあたりますので、

function hoge() {
  var local = 'ローカル変数';
}

function fuga() {
  alert(local); // ReferenceError: local is not defined
}

fuga();

localという名前の変数が無いじゃねえかゴルァーってエラーになります。なんで、関数内部で宣言された変数の有効範囲は、

function hoge() {
  var local = 'ローカル変数';
  alert(local) // ローカル変数
}

hoge();

その関数と同じ空間だけってことね。


関数外部の変数はグローバルスコープを持ち、その変数を総称してグローバル変数と言います。関数内部の変数はローカルスコープを持ち、総称してローカル変数と言います。

このようにJSで扱う変数は基本的に2つのスコープのうち、どちらかに属することとなります。


ここまでで専門用語に追いついていけなかった用にまとめておきますと、

<script type="text/javascript">
/* 関数の外で宣言された変数はグローバルスコープを持つ */
/* それらの変数を総称してグローバル変数と言う */
var global = 'だからこれはglobalという名前のグローバル変数だね';

function hoge() {
  /* 関数の内で宣言された変数はその関数のローカルスコープを持つ */
  /* 関数内部で宣言された変数を総称してローカル変数と言う */
  var local = 'だからこれはhoge関数のlocalという名前のローカル変数だぬぇい';
}
</script>

scriptタグがあると分かりやすいかな。(面倒なんでこれ以外はscriptタグを省略してます)


そしてグローバル変数はプログラム全体から参照できますが、ローカル変数はその関数内部からしか参照できません。

グローバル変数の「プログラム全体」とは、

var global = 'グローバル変数';

function hoge() {
  alert(global) // グローバル変数
}

hoge();

関数内部からでも呼び出せたりすることです。(ローカル変数の結果と混同しないように注意)

こんな感じで、ローカルだのなんだの関係なくどこからでも見えますよー参照できますよーってのがグローバル変数。


それに対してローカル変数は、もう分かりきっていることですが、

function hoge() {
  var local = 'hogeのローカル変数';
  alert(local) // hogeのローカル変数
}

/* 関数内部でalertすることはできるけど */
hoge();

/* 外部からだとどこにもないよーって怒られました */
alert(local) // ReferenceError: local is not defined

ローカル変数の有効範囲はその関数の内部だけです。

ここまでを前提に次から本題。


というかもう答えは最初から出てます。

関数内部で正しく宣言されたローカル変数は外部に漏れることがありません。それらはグローバル変数にはならないのです。

最初の例と同じコードですが、

function hoge() {
  var local = 'ローカル変数';
}

alert(local); // ReferenceError: local is not defined

関数外部からローカル変数は見えません。これは即時関数の形にしても同じ。

(function() {
  var local = 'ローカル変数';
})();

alert(local); // ReferenceError: local is not defined

カッコがついて別のものに見えるかもしれませんが、ただの関数ですから当然です。内部で宣言されたローカル変数は外部から参照できません。

もちろん内部で定義された関数も同じです。

(function() {
  function local() {
    alert('ローカル関数');
  }
})();

alert(local); // ReferenceError: local is not defined

関数も外部からは見えません。(こういった関数の中の関数はローカル関数とか内部関数と称されています)

このように変数も関数も閉じ込められます。

そういうわけで、小見出しの「即時"関数"だから関数内部の変数はローカルに限定される」の意味は、つまり、関数内部で正しく宣言された変数や関数はローカルに限定されグローバルに影響を与えることがなく、関数内部に閉じ込められるということです。

関数内部で完結するので、グローバル変数を1つも作らずに処理を行なうことができちゃうわけですね。わーいわーい。

これが2つ目のグローバル変数の使用を抑えることができる理由でした。


なお、この項目ではスコープの説明は不十分です。いろいろ省きましたが、関数内部でのvar無しの動作や重要なスコープチェーンとそれの発展としてクロージャというものがあったりします。

大切なことなので知らない人は入門書やWebで検索したりして学習してください。

スコープという概念は簡単なんだけど、それを言葉で表現するとなるとむつかしいです。早い話がコード書けば自然に身につくものなので、理屈で攻めても理解できないならコードを書くしかありません。


即時関数がグローバル変数の使用を抑えることができる理由まとめ

長いこと続いたので、以上の2つの理由をまとめると、

  • 即時関数を使えばその関数はグローバル変数に割り当てられない
  • 関数内部で宣言した変数やら関数はローカルに限定されグローバルに漏れない

というわけで、一切グローバル変数を利用することなくコード書けて便利!最高!結婚しよう!イエー!マリッジブルゥー!!!ってことでした。

まとめると簡単に言えちゃうことだったりしますが。理解しているからこそ簡単と言えるのであります。たぶんそんな落とし所です。

グローバル変数の使用を抑える意味

補足として。なんでどいつもこいつもグローバル変数を使わないことにこだわっているのかというと。


それは変数の名前が衝突すると厄介だからなのです。たとえば、404 Blog Not Found:javascript - ブログパーツ/ウィジェット開発者におねがいが良い例です。ちょっこしリンク先を見てください。

こちらでも簡単に流れを記述しておきましょう。js部分。

<script type="text/javascript" src="http://blogchart.jp/js/blogparts.js"></script>
<script type="text/javascript">
<!--
id="22";
blogurl="http://blog.livedoor.jp/dankogai/";
partstype="b";
viewBlogparts();
// -->
</script>

最初のscriptタグはhttp://blogchart.jp/js/blogparts.jsのjsファイルを読み込んでます。読み込んだコードは下記の通り。

function viewBlogparts(){
  var rs = Math.round(Math.random()*2147483647);
  var link = "http://blogchart.jp/blog?url="+blogurl;
  var imgsrc = "http://blogchart.jp/blogparts?type="+partstype+"&id="+id+"&referer="+document.referrer+"&rs="+rs;
  document.write("<a href='");
  document.write(link);
  document.write("' target='_blank' >");
  document.write("<img src='");
  document.write(imgsrc);
  document.write("' border=0 width=163 height=53 />");
  document.write("</a>");
}

この時点でviewBlogpartsというグローバルな関数が1つ割り当てられます。次にscriptタグの間にコードを書いている部分がありますね。

id="22";
blogurl="http://blog.livedoor.jp/dankogai/";
partstype="b";
viewBlogparts();

idとblogurlとpartstypeという変数は全てグローバル変数で値が代入されています。ブログパーツの設定を行なう値ですね。

設定値はブログパーツの外見やどのブログURLを対象とするのかといった設定でしょう。そしてこれらの変数はviewBlogpartsの内部で参照されていることが分かります。var linkとかvar imgsrcとかのとこです。

グローバル変数はプログラム全体から参照することができます。なのでviewBlogpartsという関数の内部からグローバル変数を参照することが可能です。


プログラムがどう動作しているのかまとめると、

  1. グローバル変数に設定値を代入
  2. viewBlogpartsの呼び出すわけですが、その時にグローバル変数を参照してlinkとimgsrcの値を生成
  3. 最後にdocument.writeでブログパーツを挿入している

という流れ。

これはやっちゃいけないですね。どこがいけないかというと、ブログパーツの設定値をグローバル変数で設定している部分です。


例えば他に読み込むjsファイルのプログラムでもグローバル変数を使っていて、その名前がidだのblogurlだのとviewBlogpartsで利用する変数とかぶっていたら値が書き換わってしまいますね。

すると予期せぬ動作を引き起こすかもしれません。というか、現に404 Blog Not Found:javascript - ブログパーツ/ウィジェット開発者におねがいの画像のような結果を招いています。

あとグローバル変数ばかり使ってると可読性がスパゲティ(コードがめちゃくちゃな感じ)になるから使うのやめようって意図もありしますが、それはまた別の話。


ちなみに、今回の問題の解決策は設定値は引数で渡すようにしたらどうかと提案してくれています。これだと、

function viewBlogparts(obj){
  var id = obj.id || 1,
        blogurl = obj.blogurl || 'http://blogchart.jp',
        partstype ~ obj.partstype || 'a';

  var rs = Math.round(Math.random()*2147483647);
  var link = "http://blogchart.jp/blog?url="+blogurl;
  var imgsrc = "http://blogchart.jp/blogparts?type="+partstype+"&id="+id+"&referer="+document.referrer+"&rs="+rs;
  document.write("<a href='");
  document.write(link);
  document.write("' target='_blank' >");
  document.write("<img src='");
  document.write(imgsrc);
  document.write("' border=0 width=163 height=53 />");
  document.write("</a>");
}

/* 使うとき */
viewBlogparts({'id': 22, 'blogurl': 'http://blog.livedoor.jp/dankogai/', 'partstype: 'b'})

オーソドックスな形ですがこうすれば引数で渡せます。これでありきたりな変数名のグローバル変数を使う必要がなくなりました。(が、こういうのはiframeでやっちゃうべきですね。たぶんね)

この事例では即時関数は必要ありませんでしたが、使って対処する方法もありますし、オブジェクト1つ用意してなんやかんやすることもありますし、手段はいろいろあります。

なんでもいいので一番しっくりくる方法がいいと思います。


なぜ即時関数はカッコ厨なのか

ここまでは、即時関数は「どういったことができるのか」ということと、「どういった効果があるのか」についてかんがえてきました。

これらの答えは、

  • 関数定義と関数の呼び出しをまとめて行なうことができる

ということができて、

  • グローバル変数の使用を抑えることができる

といった効果がある、ってことでした。


これだけ理解できれば扱う分には問題ないでしょう。

でも、「即時関数はなぜカッコ厨なのか!」という疑問が魂の叫びとしてふつふつと湧いてきてますね。きてますね!

つまり、(function(){...})()の形で即実行する理屈がわからん!とか、functionの前に" ( "があったり後ろで" )( ) "があったりして何なの!?結婚するの!?とか。

見た目で惑わされていないでしょうか。いや、惑わされているよきっと。あなたは、惑わされている!

なぜならば、この蠱惑的な姿形をもった即時関数はJS界におけるレディー・ガガ。最初に出会ったときから惑わされる(理由もなくどん引きする)様は同じなのです。

それでも、この世は実に素晴らしく、誰もが即時関数の惑わしから解き放れていないわけではありません。俗物から解脱した聖人もいらっしゃるのでございます。なのでWebで検索すればたいていどなたかがなんでカッコ厨か言及しております。

聖人バンザイ、ブッタバンザイでございます。般若心経を唱えたい一心を抑えて、聖人様方の言い分をまとめますと、

  • 関数を式として評価させるため

だからカッコ厨なのです。

これが解脱への第一歩ですね!さあ、解脱したらブッタの髪のボツボツをほどく作業に戻るんだ!バリカンで一度に剃ろうとすると固すぎて刃が折れちゃうからね!そのためにも関数が式だかなんだかをさっさと学習しなくっちゃね!

ところで式ってなんでしょう?


関数には、というかJSには式(Expression)と文(Statement)が存在する

関数というかプログラミング言語には(他の言語はいざしらす少なくともJSには)式と、それから文というものが存在します。

文といえばただの文章を思い浮かべたり、式というと数学で使う数式が思い浮かぶかもしれませんが、それだけのイメージだとだめです。具体的にどうだめなのかというと私と一緒です。


・・・ね、だめでしょ(´;ω;`)

少し話が膨らんで長くなりますが大事な概念なので式と文についてみていきましょう。

式(Expression)と文(Statement)って何だろう?

プログラムを構成する要素は式と文に分類できまして。JSにおける式と文は次の通り。

リンク先は意味わからんかも。最初のうちはわからないものです。慣れてくるとね、うん、やっぱりわからなかったりします/(^o^)\

もうちょっと具体的にわかるよう例を交えて示します。

式とは何だろう

式とは何かと言われて何を思い浮かべるでしょうか。

もしかしたら、

100 + 200 = 300

みたいな式を思い浮かべたかもしれません。これはいわゆる数式の類ですね。

このように「式=数式」といったようなイメージしか持っていなかった人。

大丈夫。そんな人たくさんいる。私とか、私とか、私とか、あと私とか私とか。私が10人くらいいるから。ある意味やばいけど安心!マイノリティじゃないよ!!大丈夫!NARUTO読んで勉強したし、スラムダンクでフンフンディフェンスのコツ考えてたし!


そいでJSにおける式とは、上記のような数式も含めて、

  • 値を持つもの

のことを意味します。


具体的に例を挙げると、

'あいうえお' // 文字列
100            // 数値
true            // 真偽値
null
[1,2,3]
{a:1, b:2}
count         // 変数の参照

これらは式です。

「え?そ、それ式?まじで?ま、まじで式なの?「100」ってあるだけで式になるの?え?数値だけで式?文字も式?ええ?それになに?変数名がぽつんとあるだけでもし、しぃきなのおおおおおおお!?えええええ!?とりあえず結婚するううううう!?」

って発狂してもおかしくない。と、経験者は語る。(はふー)

数式だけを式として認識しているとあかんのです。


それから、値を変数に代入するのも式です。

x = 100
y = 'あいうえお'

= みたいな記号があるから式だろうとまだ思えるかな。このように代入は式として扱います。(後述しますが、varをつけたりセミコロンつけたら文になります)


また、

100 + 100
'あいうえお' + 'かきくけこ'
50 < 100
'あいうえお' === 'あいうえお'

これらも式です。なんとなく式と言われたらそうだろうと思えますが。演算子あると式っぽい感じします。


「100」や「'あいうえお'」のように = や + や - などの演算子が無いただの数値や文字列も、「x = 100」や「100 + 100」といったものも全て「値をもっている」ので式です。

なんでこういったものが式なのかというと、そのように定義されているからです。それ以外に言葉がないですね・・・。うーん。たぶんこの概念はプログラミングだけじゃないと思うのですが・・・。

やっぱりわかりにくいですね。そういうわけでわかりやすいように考え方を変えましょう。「式とは値を持つもの」と言いましたが、言い換えると「式とは値を返すもの」とも言えます。

でも値を返すって具体的にどういうことなのでしょうか。これはChromeのコンソールやFirefoxのWebコンソールで試すとわかりやすい。


たとえば、100と入力して実行すると、

f:id:sandai:20110817162152p:image

といったように100と「返ってくる」でしょう。( > xxx の部分が入力した文字で、その下が返ってきた値)


他には、

f:id:sandai:20110817162153p:image

文字列の値が返ってきました。


見た目が式っぽいやつも、

f:id:sandai:20110817162154p:image

それぞれ値が返ってきています。


x = 100 も式だと例に挙げていましたが、

f:id:sandai:20110817162155p:image

こうして値を実際に返すからです。ちょっとよくわからないかもしれませんが、式全体は値を持っているのです。


そして、

f:id:sandai:20110817162156p:image

といったように x = y = 100みたいな記述が可能なのは代入が式だからですね。


このように式とは数値、文字列、オブジェクト、変数などそれぞれ1つ1つを表し、

100

さらに、それらは演算子によって代入や加算や比較などさらに「複雑な式」(これも値を持つもの、返すものの意味での式です)を構成し、

x = 100 + 300

また、これらは値を返すという特徴を持っていて、

f:id:sandai:20110818200924p:image

式とは値が返すもの、あるいは、値を持つものとまとめて言うことができます。


こういうわけですが、やっぱり難しいかもしれません。最初はこうして式とは値を持つものといわれてもなんのこっちゃ分からんと頭をかかえるものです。

そのような場合は、

x = 変数に格納できるやつだいたい式っす 

と思っていれば簡単。あるいは、

/* xに入れることができるのだいたい式っす */
if(x) {}

でも。ifの方が正確かな。式というのは結局値です。変数に格納したり条件式に使えますからね。

文とは何だろう

分類すると大きく4つに分けられます。(わかりやすいよう非公式に分けてるだけ)

  • 1. 式の後ろにセミコロンがついたら文
  • 2. 空文
  • 3. varをつけた代入文
  • 4. ブロック文, if...else 文, switch 文, for 文, do...while 文, while 文, label 文, break 文, continue 文, for...in 文, for each...in 文, with 文, コメント, throw 文, try...catch 文など

覚えなくてもいいので式との違いをつかんでください。

1. 式の後ろにセミコロンがついたら文

式のケツにセミコロンがつくと文となります。

/* 式 */
x = '式'
100
'あいうえお'
100 > 50

/* 文 */
x = '文';
100;
'あいうえお';
100 > 50;

セミコロンが無ければ式ですが、あれば文となります。なんか変な話ですけどそういうものなのと決められているので、そういうものだとしか言いようがありません。

納得したいのであれば、根本的な理由ではありませんが、

if(1 + 1;); // syntax error

これならなんとなく納得できますよね。条件は式しか記述できませんのでエラーです。

このような文を厳密には式文と言います。

上の例は全て文法上認められた文であるというだけの無意味な文です。代入の例はともかく、実際にプログラム中にこういった記述をすることはありません。たんなる例です。


JSでは制御文や関数宣言を除けばだいたいこの式文の集まりですが、" { "とfunctionキーワードが文の頭にある場合は式文として解釈されません。なぜなのかというと、それは書いてあります。

Block と曖昧になることから、 ExpressionStatement は大括弧 "{" で開始することはできないことに注意。また、 FunctionDeclaration と曖昧になることから、 ExpressionStatement は function キーワードで開始することもできない。

http://www2u.biglobe.ne.jp/~oz-07ams/prog/ecma262r3/12_Statements.html#ExpressionStatement

つまり、" { "はブロック文(ifやforや関数定義で使う { } のこと)と、functionキーワードは関数定義とかぶるのでそれらが文の最初にあるときは式文としないよーということです。

2. 空文

セミコロンだけでも文です。独り立ちしてます。一本足で立ってて立派です(´;ω;`)ブワ

;

この文は厳密には空文と言います。これが文かよとちょっとビックリしちゃいますが文です。

空文は何も行わないことを「行なう」文です。変な日本語ですが、文の定義上そう表現するのが適しているかなと。まあ結局何もしないってだけです。

文法として覚えていなくたって支障はありません。あとC系の流れを組む言語にはだいたい空文があるんでJSに限った文ではないようです。

3. varをつけた代入文

代入のうち、

x = '式'
y = '文';

といったようにセミコロンある無しで式と文に分けられましたが、varをつければいずれも代入文となります。

var x = 'これは文'
var y= 'これも文';

正しくはvar文(変数文)と言います。セミコロンが無い場合でもvar文は自動でセミコロンが挿入されることになります。(式文でも挿入されますね)


あと、これまでvar無しで代入のコードを使ってきましたが通常はvarつけます。つまり変数宣言(この場合宣言とはvarをさす)していないのに代入することはまずないということです。

var x = 'varをつかって代入しているのでおっけーです;

var y;
y = 'varで宣言されている名前への代入なので、varがこの行に無くてもおっけーです';

z = '単なる代入はまずしません';

どこでもよく言われていることなので蛇足かもしれませんが念のため注意書きしておきます。

4. ifやforなどその他愉快な仲間たちの制御文

ブロック文, if...else 文, switch 文, for 文, do...while 文, while 文, label 文, break 文, continue 文, for...in 文, for each...in 文, with 文, コメント, throw 文, try...catch 文などは文です。

Mozillaさんでまとめてあるので、

をご覧ください。この他にも制御文は存在しますが主旨と関係ないので特に言及しません。


以上がJSにおける文ですが、文とは何かと一言ではなんのこっちゃ表せなかったりします。

困ったので本でも確認したのですが、「副作用が伴うもの」とか「JavaScriptに何かさせるもの」が文だそうです。抽象的すぎて伝わりにくい。

またまた困ったので、wikiで調べたら、

プログラムにおける文(ぶん、statement)とは、コードの記述単位の1つ。1つの文が1つの手続きを表すことが多い。

.

.

.

大まかに言えば、一つ以上の式や関数呼び出しで作られる、手続き構造の分割できない基本単位が文である、と考えてほぼ差し支えない。

http://ja.wikipedia.org/wiki/%E6%96%87_%28%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%29

といったことだそうです。いくぶん分かりやすいかもいれない。

そんなわけで本も含めて得た情報をまとめると、

  • 文とはJavaScriptの実行における基本単位で1つ以上の式や関数呼び出しなどからなるもの

と、言えますが。んー、分かりやすく表現できなくて申し訳ない。言葉にするとなるとちょっと難しいです。


とりあえず、式と文の言葉の区別がつけば問題ありません。式文だの空文だのといったのはこれから出てくる用語の説明ためですので、無理に覚えたりしなくても大丈夫です。

個人的にはJSにおける文と式の境界線はさほど明確ではないように思える(実際には式文は式の値を返すし、ブラウザだと文でもundefinedを返すし)ので、ここまではっきり区別することもなかったかなというところですが・・・。


難しい。どう言い切ってよいものやら。とりあえず着地はしたので文は終わり。話膨らみすぎたかな(;´∀`)

ここまでの式と文のまとめ

長くなったのでおさらい!

えーと、即時関数がなぜカッコ厨なのかといえば関数を式として評価するためでした。ほいで式ってなんだよという話になり、JSにおける式と、それから文の概念を学びました。

  • 式とは値を持ち文や式の一部を構成するもの
  • 文とはJavaScriptの実行における基本単位で1つ以上の式や関数呼び出しなどからなるもの

まとめるとこんな感じです。


以上のことを頭において即時関数のカッコ厨の理由と、それに関連する関数の式と文について話を進めていきます。式と文の意味が分かっていれば簡単です。

あと、これまで式と文のうち関数は一切例として挙げていませんでした。というのも、関数には式と文の2種類あるからです。それらの見た目は全く一緒なので、見分けがしづらいというか、微妙なのです。

ぶっちゃけ面倒だったのでまとめて書きたかったという個人的な都合です。そんなわけで、次で即時関数のカッコ厨と関数の式と文ともろもろまとめて終わり。


なぜ即時関数はカッコ厨なのかリターンズ

同じ見出しが上の方にあるんでリターンズで。


関数には式と文の2種類あります。そのうち、関数定義と呼び出しをまとめて行うようにするには関数を式として評価させなければなりません。なぜなら文のままだとエラーになっちゃうからです。

まずはこれがどういうことなのか確認。

関数宣言で即時関数のような動作ができないわけ

関数の文とは関数宣言のことです。

function hoge() {
  alert('関数宣言です。関数の文という位置づけのものです');
}

即時関数は関数定義と呼び出しをまとめて行なうものですよね。関数宣言でもそうしたいのであれば、ケツに関数呼び出しの演算子である( )をつける形になります。

そうしてつけてみると、

function hoge() {
  alert(1);
}(); // syntax error

あひー!!エラーになっちゃった!syntax errorとは構文エラー。つまり「決められた通りに式と文が書かれていないよー」といった意味です。

なぜエラーになったかというと、JSでは、

function hoge() {
  alert(1);
}
();

このように改行が入っているみたいな解釈がなされているんですね。( )は関数呼び出しを行なう演算子ですが、文の先頭なので呼び出しもクソもないわけです。だから文法エラーになっちゃったのです。


でもなんで改行が入ったように解釈されたのでしょうか?井手らっきょのせいでしょうか?

うーん、それは否めないけれども、いやむしろそうであったら「裸になること禁止令」を出せば良いだけれども、これは( )だったからこうなったとか特別な動作ではなく、たとえばセミコロンでも同じ結果になります。

function hoge() {
  alert(1);
}; // セミコロン

/* どう解釈されるかというと↓ */

function hoge() {
  alert(1);
}
; // セミコロン

空文になっちゃいました。このように、関数宣言における終端の波括弧(" } ")で文は終わりとなっていまして。正確に言えば関数宣言そのもので文は完結しているのです。


んーと、これは波括弧だからいまいちイメージが沸かないかもしれませんが、改行が入る現象を別の文で表すと、

/* 関数ではありませんが、上記の改行が入る形になる現象は、 */
/* 結局こういうことになってるのと同じ */
var x = 1; + 2;

/* これがどう解釈されるかというと↓ */

var x = 1;
+ 2;

当たり前ですがセミコロンの後に続けて式が書けるわけがありません。

関数宣言に( )をつけて書くというのは、これほど無意味なことをやってるに等しく、文の後ろで式が(正確には文が)書かれていることと一緒です。(上記のコードは関数宣言における終端の波括弧とセミコロンが同じという主張ではなく、「文の終わり」という点だけが共通していることを示した例です)

なので、同じように( )でもセミコロンでも関数宣言とは別の文として解釈されたのです。なんとなくイメージが湧いたでしょうか。


こうして関数宣言ではできないため関数を式として評価させることで即時関数の動作を実現するに至り、めでたく井手らっきょはダグトリオに進化しました。目新しい!やったね!ドッゲドッゲブリュリュイイイイイ!!!

終わり。



じゃなあああくて。

関数宣言だと即時関数のような動作を実現することはできないとわかりました。だから、関数を式として評価させることで( )をケツにひっつけて呼び出せるようにするのです。

では具体的にどうであれば関数は式となり、あるいは、文となるのでしょうか。

関数における式と文の関係

その関数が式なのか文なのかが決まるのは、functionという文字が行の先頭にあるかどうかという点です。行の先頭にあればそれは関数宣言(関数の文)となり、それ以外であれば関数は式となります。


なぜそう言えるのかというと、はっきり書かれていませんが式文の項でこのように書かれています。

...また、 FunctionDeclaration と曖昧になることから、 ExpressionStatement は function キーワードで開始することもできない。

http://www2u.biglobe.ne.jp/~oz-07ams/prog/ecma262r3/12_Statements.html#ExpressionStatement

「functionキーワードが行の先頭にあると関数宣言とかぶるので式文としない」とあります。

つまり関数定義のうち「functionキーワードが行の先頭にあるならば関数宣言である」と言えますね。これを言い換えると関数定義のうち「関数宣言でないのであればfunctionキーワードは行の先頭ではない」とされ、関数は文と式の2種類しかありませんので、行の先頭にfunctionがなければそれは関数宣言ではなく式であると導けます。


そういうわけで、functionが行の先頭にあるなら関数宣言となり、

function hoge() {
  alert('functionが行の先頭にあるので関数は文です');

  function piyo() {
    alert('関数の中の関数でも、行の先頭にあるので関数は文です');
  }
}

function fuga() { alert('インデントがなくても行の先頭にあるので関数は文です'); }

それ以外なら関数は式であると言えます。

var hogera = function() {
  alert('functionの前に = や var があるので関数は式です');
};

[
  function() {
    alert('配列に格納されてるときもfunctionより先に [ があるので式です'); 
  }
];

if(function() {alert(1); }){
  alert('こんな不細工なことやりませんが式なので条件式にも使えます');
}

(function() {
  alert('(1+ 1)と同じ意味での( )で関数をくくれば、functionより先に ( があるので式となります');
});

雑な言い方になりますが、functionより前に何か文字がなければ関数宣言ですし、なんかあれば関数は式になるって感じです。

それから関数宣言で関数名は必要ですが関数の式では省略できます。(関数の式で名前をつけるとしたら何か目的を持ってつけます。特に付ける理由がないのであれば無名で問題ありません)


このようにその関数が式なのか文なのかといったことは単純な仕組みだったりしますが、慣れないうちは混乱することがあるかもしれません。しかしほぼ文法上の違いですから、関数は式でも文でも機能は変わらないと意識しておけば問題ないです。

即時関数の仕組み

ここまでで関数を式として評価させる理由と、その方法を確認してきました。

式として評価させる理由は、

  • 関数宣言のままだと関数呼び出し演算子の( )を使うことができないから

で、関数を式として評価させる方法は、

  • functionキーワードを行の先頭におかない

というものでした。


こういったことが理解できれば、カッコなばけものに見えた即時関数もどういった仕組みで成り立っているのかみえてこないでしょうか。


まず関数を式とするために (1 + 1) といったように利用するグループ化演算子の( )で関数をくくります。

(function() {
  alert('関数を( )で囲んでいます。この時点で関数は式となっています');
})

functionより前に ( があるので式となりますね。そうして、関数呼び出しの演算子( )を最後にくっつけることで、

(function() {
  alert('関数呼び出しの演算子である( )をくっつける');
})(); // ←関数呼び出しの( )ね

関数定義と関数呼び出しをまとめて行なうことができる即時関数ができました。

(1 + 1)といったように使うグループ化演算子の( )と、hoge()といったように使う関数呼び出し演算子の( )の区別がついていないと、単なるカッコ厨に見えてしまいますが区別できればわかりやすいですね。


さて、関数を式として評価させるために即時関数ではグループ化演算子の( )を使って関数全体をくくっています。でもよく考えたらこれって別に( )でくくらなくてもいいですよね。

関数を式とするにはfunctionキーワードを行の先頭におかなければ良いので、その他にも方法はあるわけです。

たとえば、

+function() {
  alert('これでも即時関数です。実はカッコである必要はないのです');
}();

こうしても問題ありません。functionの前に + があるので見た感じだとエラーになりそうですが、関数は正常に呼び出すことができます。その他同じような例としては、即時関数(function(){ ... })()の別の書き方いろいろ: Architect Noteにいくつか紹介されているのでどうぞ。


このように必ずしも即時関数はカッコ厨である必要はありません。カッコで関数をくくることは式として評価させるための、いくつかある方法のうちの1つにすぎないのです。

とはいえ一般的に使われているのはカッコでくくる方法なのでその習慣に習う方が良いでしょう。


なぜ即時関数はカッコ厨なのかまとめ

即時関数はなぜカッコ厨なのかいえばそれは関数を式として評価させるためでした。

即時関数は関数定義のあとにすぐ呼び出すコードです。関数宣言のまま関数呼び出しの演算子で呼びだそうとすると、それらは別々の文として解釈されてしまうためエラーになります。

function hoge() {
  alert('関数宣言');
}
(); // syntax error!!!

そこで定義の後にすぐ呼び出しができるよう関数を式として評価させてます。そして、その方法はカッコで関数をくくるというものでした。

(function hoge() {
  alert('即時関数どす');
})();

これが一般的に即時関数と称されるコードです。

ですが、関数を式として評価させる方法はカッコでくくる以外にも方法があり、

+function() {
  alert('これでも即時関数');
}();

このような書き方でも即時関数として利用することができるのです。

関数をカッコでくくるのはいくつかある方法のうちの1つにすぎません。ただし、一般的に使われているのはカッコでくくる方法なのでその習慣に習って利用した方が良いと言えるでしょう。


といったところで終わり。

ポイントは式と演算子と文ですね。これらが何となくでもわかれば文法上の悩みは消えるでしょう。黒魔術的なコードについては、えー黒魔術師を目指すのであればがんばってください。JSは黒魔術が結構あるので楽しいかもしれません。私は拒絶反応がでるので止しときます。たまねぎ剣士でいいんです。


ここまででかなりくどく書いてきましたが、なるべくコードではなく言葉で表現したつもりです。こうすると冗長になってしまってだいたいは読むのをやめるもんですが、ここまで読んでくれたあなたは優しい。

よし、井手らっきょ2号の称号をあげます。ええ、ええ、わかっています。そんな感謝だなんてあははは。同じ井手らっきょ仲間じゃないですかあ。あははははあ。

・・・というわけで、まあ、マイノリティに悩んでいる人のお役に立てれば幸いです。

厳密には関数宣言は文ではない

今までの流れをぶったぎるような内容だけれど、実際のところFunction Statementという語句はありません。

JSだとどうなのかなと検索してみると、2つほど見たことあるサイト名があがってきまして。MDCやmsdnですね。どうやらstatementとして扱っているようです。

JSでは文として扱っても問題ないということなのでしょう。というか文なのかな?静的な関数にそれはどうなのかとも思いましたが、文として広まっているし理解しやすい方がいいのでこの記事でも文としておきました。


関数定義の名前

定義については以下のページを参考。

機能としては全て関数を定義するって部分は一緒なのですが、それぞれ姿形が違ったり効果も微妙に違いがあるのです。

関数宣言

よく見かける普通の関数の構文。関数宣言と言います。

function hoge() {
//
}

ケツにセミコロンはいりませんので。関数式と比べるとパフォーマンスは良いみたい。特別な理由がない限りこの書き方が基本。

関数宣言の特徴として巻き上げが挙げられます。

alert(hoge); // function hoge() { return 1; }
var hoge = 1;

function hoge() {
  return 1;
}

alert(hoge); // 1

定義のタイミングが関数宣言で次が変数って流れ。

関数式

関数リテラルとも言われます。あとMDCではfunction演算子とかも言われてますが。んー、function operatorは良い表現とは思えませんので私は非推奨です。リテラルもなんか伝わりにくい。というわけで関数式おすすめ。


変数に関数を代入する構文です。JSらしい書き方ですね。

var hoge = function() {
//
};

無名関数でないケースもあります。「名前付き関数式」って言われていたりしますが、名前があろうが無かろうが関数式と抽象的に表現したりもするんで、ゆるゆるです。

var hoge = function fuga() {...};

この場合fugaはこの関数の内部からしか利用できません。だから通常は、

var hoge = function fuga() {
//
};
fuga() // error

となってしまうわけです。が、これJScriptでは漏れます。


あとね、セミコロンは必須です。無いと、たとえばだけど、

var hoge = function() {
  alert('セミコロンどこいったーヽ(`Д´)ノ');
}

(1 + 1)  * 2 ;

としたら関数が呼び出されます。こんなコード後ろに書かないけど、もしなんか書くことあったらふわっとつけときましょう。というか常につけます。。


これらは関数式というかそもそもはvar文なのですが、なんでもかんでもvar文つってたら分かりにくいので特徴を表している関数式の名前で良いと思います。

Functionコンストラクタ

これは関数定義というわけではありませんが、関数を生成します。明確な目的がない限りまず使いません。

var x = new Function('y', 'z', 'alert(y + z)');
x(5,3);

知らなくても大丈夫です。

メソッド

JSではオブジェクトのプロパティが関数のときメソッドと称します。

var hoge = {
  piyo: 123, // プロパティ
  fuga: 'あいうえお' // プロパティ
  hogera: [1,2,3]; // プロパティ
  hogehoge: function() {...} // メソッド
};

メソッドであっても実際はプロパティですとか言うとややこしくなるけど、まあそんなややこしさの爆弾をJSは抱えています。プログラミング言語ってここまで定義が曖昧というか整理されていないものなんでしょうか。


(function(){}())と(function(){})()について

かの有名なダグラス・クロックフォードさんは、

(function() {
//
}());

ってつかえよーうって推奨してます。Lispの面影を感じる今日この頃。たぶん意図はあると思うんです。

でも見辛い。従来に慣れたんだ。嫌だ。やめてくれ。ダグラスさん、まだあっしは、あっしはこの既成概念から逃れることのできねえ俗物なんでごぜえますだ。

といったように身も蓋もない思いでこざいます。

申し訳ないのですが、これからさきどちらが広まるか知りませんけれども、今回は従来の形式を利用させていただきました。皆様はぜひともこの俗物のごとく嘆かわしい人物とならぬよう身を清め生きることをお薦め致します。


くそったれ長い!

大丈夫だ!長いと思うから長く見えるんだ!長くないと思ったら・・・・・やっぱり長い!神様に誓って長い!でも良いんだ!そんなことより暑いからアイス食べよう!おいしい!ありがとう!アイスの神様!というか最近涼しい!たぶんきっとエアーマンのおかげだと思う!ロックマンありがとう!


参考

fratforestfratforest 2011/09/03 15:40 (function(arg){alert(arg);}('良い記事')); 想いを直ぐに伝えたい。

sandaisandai 2011/09/08 21:15 for(var i=0,s='ありがとう'; i < s.length; i++)(function(s) { alert(s)})(s[i]); 感謝は細目に返したい。

mskmsk 2012/10/29 21:37 もあもあと抱えてた疑問が全てまるっときれいに解決しました。
レディー・ガガのところでふきました。
面白素晴らしい記事ありがとうございます!!!

hanishihanishi 2012/10/31 16:40 !function(a){alert(a.same)}(a);でも同じ?

774774 2013/03/26 08:30 ただのスコープ
{
alert('hoge');
}
との違いにも触れてほしいです。

silversilver 2013/06/26 17:18 ()()の意味が知りたくて検索したらここに飛んできました。
とても素晴らしい良記事でした。おかげで理解することができました。

ただ結婚、結婚しつこいわw

あ 2013/07/15 19:41 この人好き

あるるあるる 2013/07/29 18:38 素晴らしい記事でした。ありがとうございました。

fa11enprincefa11enprince 2014/07/22 03:59 JS初心者にありがたい記事でした!!

fluorianfluorian 2015/06/03 14:33 最近のトレンドは
let($=jQuery){
$
}

yuyu 2015/07/05 20:02 わかりやすかったです!ありがとうございました!

hanahana 2015/07/27 22:06 感謝!
つぎは「promise」のわかりやすい解説をお願いします。

通りすがり太郎通りすがり太郎 2015/08/05 19:08 +functionという表記について知りたくて辿り着きました。
かなり長かったので読み疲れてかなり毛は抜けましたが、
かなり為になりました。

ありがとうございます。

なっちなっち 2015/11/16 16:18 +functionの意味が分かりました。ありがとう!

もぶみもぶみ 2015/12/31 00:29 ものすごく分かりやすかったです!!

よういちよういち 2016/02/04 14:57 すごくわかりやすかったです!

nanacy7741nanacy7741 2016/03/10 17:22 がるるるるるるるるるるっっ

shinshin 2016/05/23 16:24 ここまで徹底して書けるとは、すごい、こわい、おもしろい!

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証