即時関数が使われるケースまとめ※追記:2017.03.31本記事の内容はES5(ECMAScript5)の仕様に基づいています。ES6(ES2015)以降ではlet、constで宣言した変数のスコープはブロックスコープになるため、「即時関数はスコープを汚染せずに新たなスコープを作成するための唯一の手段」という表現は当てはまらなくなります。JavaScriptには「即時関数」という構文があります。即時関数は関数を定義すると同時に実行するための構文で、この即時関数を使ってコードを書いたことのある方も多いのではないかと思います。JavaScriptに慣れている方にとっては「何を今さら」といった書き出しかもしれませんが、私はこの即時関数を初めて知った時、その必要性がイマイチ見出せませんでした。それは、「関数を定義と同時に実行するのは分かるけど、別に普通に関数を定義して、その関数を呼び出せばいいじゃん」って思ったからです。確かに、一度しか使われないような関数をいちいち名前付きで定義してそれを呼び出すというコードは冗長的かもしれません。そのような場合は即時関数を使った方がよりスマートなコードになるでしょう。それだったらそもそも関数にしなくても良いのでは?などと考えていたので、私は即時関数を「再利用しない関数を実行する場合のコード量を減らすための手段」程度にしか考えていませんでした。オープンソースのコードを見ると、結構至る所で即時関数が使われていたりします。私から見たらかなり「メリットの薄い」即時関数がなぜ世間では当たり前の様に使われているのかが理解できなかったのですが、自分で実際に即時関数を使い始めてみてようやくその「使いどころ」的なものが分かってきました。今回は、この「即時関数」について、少し掘り下げてまとめてみました。即時関数の構文まずは簡単なおさらいとして即時関数の構文を見ておきましょう。以下に即時関数の書き方(2パターン)を示します。//関数の中身・・・})();一見両者の違いが分かりづらいですが、最後の括弧の位置が異なります。どちらも即時関数の構文としては問題ありませんが、JSLintでは前者の書き方が推奨されます。function.」と警告されます。)また、即時関数は定義の後すぐに実行され再利用もされないため、関数名は不要です。即時関数に引数を渡す場合は、以下の様にして渡すことが可能です。...);また、即時関数も通常の関数同様、戻り値を持たせることもできます。即時関数はスコープを汚染せずに新たなスコープを作成するための唯一の手段JavaScriptには他の言語にあるようなブロックスコープがありません。あるのはグローバルスコープと関数スコープだけです。言い換えると、関数は任意にスコープを作るための唯一の手段だと言えます。即時関数も関数ですのでスコープを提供します。関数スコープの中でvarを使って定義された変数は関数の中でローカルな変数になるので、関数の外側の変数を上書きしたりすることはありません。コードの中にある一連の処理があるとします。これらの処理がいくつかの一時変数を使用する場合、これらの変数を全てグローバル変数にしてしまうのはアンチパターンです。このようなことを繰り返していると、コードが大きくなるにつれグローバルスコープが不要な変数名であっという間に埋め尽くされてしまい、コーディングがしづらくなるだけではなく、予期せぬトラブルの元にもなります。変数の有効範囲は可能な限り局所的にすべきです。利用していることを示すコードです。//「3」が出力される上記のコードは期待した通りに動作しますが問題があります。b)をグローバルスコープ上に定義してしまっていることです。そのため、もし上記コードよりも前の部分で同じ変数名が定義されていた場合、その変数を上書きしてしまうことになり、予期せぬ結果をもたらす場合があります。b)をスコープの中に閉じ込めるようにした場合の例です。//「3」が出力されるこのコードは最初の例よりかは幾分マシです。b)が関数スコープの中に閉じ込められているので、関数外部の変数を上書きしてしまうようなことは有りません。関数の実行後に一時変数がグローバルスコープ上に残り続けるような事もありません。この場合も結局「calc」という関数を格納するための変数をグローバルスコープ上に定義しているので、最初のコード例と同じ問題が発生する可能性があるため、あまり良い方法であるとは言えません。calc関数が再利用されないようなものであればなおさらです。そこで、このコードを即時関数を使って書き換えたものが次のコードです。//「3」が出力される。いう無駄な変数を宣言する必要がなくなります。グローバル変数であり、それ以外に新たに作成されたグローバル変数は有りません。コード例2とコード例3はどちらも関数を使い、新たなスコープを作成してその中に一時変数を閉じ込めていることには変わりないのですが、コード例2の方は関数そのものを格納するための変数をグローバルスコープ上に作成しているため、グローバルスコープを汚染してしまっています。これに対し、コード例3の方ではグローバルスコープを全く汚染せずに一時変数を関数スコープの中に閉じ込めることが出来ています。即時関数は現在のスコープを汚染せずに新たなスコープを作成することができるということです。このようなことができるのはJavaScriptでは即時関数だけです。今回の例の様に、一連の処理を即時関数で包み一時的なスコープの中で実行することは、プログラムをスコープ上のサンドボックス内で実行することになるため、スコープの外側へ影響を与えてしまうことを防ぎます。即時関数が必要とされるのはこのためです。即時関数は確かに「関数」ではあるのですが、通常その用途は名前付き関数とは異なります。一般的な名前付き関数が再利用を前提とした一連の処理を定義するために使われるのに対し、即時関数は再利用されない一連の処理を新たなスコープに閉じ込めることを目的として使われることが多いと考えられます。スコープという観点から見たら、どちらも新たなスコープの中に処理を閉じ込めているのですが、コードが再利用されないのであれば、現在のスコープを汚染しない即時関数を使った方が良いでしょう。再利用されない一連の処理を新たなスコープで包み込む即時関数が使われるケース即時関数を「現在のスコープを汚染せずに新たなスコープを作成するためのテクニック」と捉えるといくらでも使いどころがありそうですが、むやみに即時関数を使うのはかえってコードの可読性を低下させてしまうことにつながるのではないかと思います。(即時関数内部のコード量が増えてくると、関数の範囲を確認するために何度も画面をスクロールしなければならないはめになるからです。)したがって、即時関数を使う場合はある程度使いどころを見極めたうえで使っていくことが大切なのではないかと考えます。以下は一般的に即時関数がよく使われるケースの一例です。以下はページ上のボタンをクリックした時に現在日時を表示するコード例です。};}());ページが読み込まれた際にページの初期化(イベントハンドラの設定)を行っていますが、いくつかの一時変数を定義しています。これらの一時変数は初期化完了後にはもはや不要なので、グローバル変数として定義するのは好ましくありません。そこで、初期化コード全体を即時関数で包むことで初期化完了後に一時変数などの不要な副産物を残さないようにしています。)を事前に取得しておくケースは良くあるかと思います。以下の例は閲覧中のブラウザの種別(ベンダー)を取得するためのコードです。(※大抵の場合、この手の処理はサードパーティー製のJavaScriptライブラリが行ってくれたりするので、自前で実装することはあまりないかもしれませんが。それぞれグローバル変数として定義しています。値が確定した後は不要なので、グローバル変数として定義するのは好ましくありません。格納するようにした例が次のコードです。このようにすれば、結果を得た後に一時変数が残るようなことは有りません。いった「アクセス修飾子」はありません。そのためオブジェクト内に定義されたプロパティ/メソッドは、基本的には全てパブリック(public)なものとして扱われます。即時関数や、クロージャといったJavaScriptの基本テクニックを利用することで簡単にプライベートなプロパティ/メソッドを定義することが可能です。increment、decrementという2つのメソッドをもった単純なカウンターオブジェクトを作成するとします。incrementメソッドは現在のカウントに1を加え出力し、decrementメソッドは現在のカウントから1を引いて出力します。この内部変数はincrement/decrementメソッドからのみアクセスを許可し、オブジェクトの外部からのアクセスを拒否するものとします。(つまり、プライベート変数にしたい)以下に、このような機能を持ったオブジェクトを即時関数を使用せずにオブジェクトリテラルを用いて作成した例を示します。//※countプロパティを直接参照/書き換えが出来てしまう上記のコードは見かけ上は期待通りに動作しますが、ひとつ問題があります。それは、現在のカウントを保持するための内部変数(count)がパブリックプロパティになってしまっているため、オブジェクトの外部から容易に参照、上書きが出来てしまうということです。プライベートプロパティとして実装したいところですが、JavaScriptにはそのような構文が存在せず、オブジェクトリテラル表記で定義されたプロパティ/メソッドは全てパブリックになってしまいます。そこで、このオブジェクトを即時関数(+オブジェクトリテラル)で作成するようにした例が次のコードです。プロパティに相当するものを関数内のローカル変数として定義し、increment/decrementメソッドをオブジェクトリテラルを使って定義しています。いう2つのメソッドを持ったオブジェクトとなり、coutプロパティは存在しません。アクセスする手段がないことを意味します。クロージャとして存在し続けるので、現在のカウントを保持させることが可能です。JavaScriptには変数のプライバシーを定義するための構文はありませんが、このように即時関数やクロージャといったテクニックを利用すれば比較的簡単にプライバシーを実現することができます。まとめ即時関数は、名前を持たない関数をその場で実行しているに過ぎないため、本質的には通常の名前付き関数と同じです。そのため、「即時関数を使わなければ実現できない処理」といったものはあり得ません。それらは即時関数でなくとも通常の名前付き関数で処理することは可能です。即時関数は名前を持たないため、現在のスコープを汚染せずに新しいスコープを作成することができます。このことが通常の名前付き関数との違いであり、即時関数ならではの用途を生み出します。即時関数は本質的には通常の名前付き関数と同じ。即時関数の主な用途は、再利用されない一連の処理を新たなスコープで包み、プログラムをスコープ上のサンドボックス内で実行すること即時関数を使って、プログラムのスコープを適切に制御することができれば、コードの見通しやメンテナンス性を改善するすることに役立ちます。2016年9月30日サイトマップ。