仲間内で行った説明が好評だったので、Qiita用に纏めてみました。
#即時関数って?
通常の関数は、あらかじめ定義しておいて後で実行する形をとりますね。対して即時関数は、その名の通り関数定義後にそれを即実行するものです。無名関数でこのメソッドを使用することによって、名前空間の汚染を気にせずに変数を使えるといったメリットがあります。以下のような書き方が主流だと思います。
(function() {
...
})();
(function() {
...
}());
しかし、あくまで主流です。即時関数の仕組みさえ理解していれば、もっと違う書き方も可能なのです!
まずは、前提となる無名関数の特徴についてから説明します。
#無名関数について知っておくべき前提
無名関数は、それ単体だけ書くとエラーを吐いてしまいます。
function() {
...
}
//Uncaught SyntaxError: Function statements require a function name
しかし、functionが行頭に無いならばエラーは吐きません(勿論そのままでは「ただそこにあるだけ」で、実行はされません)。
!function(n) {
...
}
[function(n) {
...
}][0]
0 == function(n) {
...
}
(function(n) {
...
})
#本題:括弧の正体
まずは関数を変数として宣言してみましょう!
var a = function(n) {
...
}
次に、宣言した関数を使ってみましょう!
a("引数");
関数を使用する際、皆さんは関数名(引数)
という形で使用しますよね。この最後の括弧は、functionというオブジェクトに対しての「実行せよ!あっ、引数も受け取っておいてね!」という命令なんですね。事実、括弧無しで関数名だけポンと書いても、関数は実行されません。
a;
//何も起こらない
じゃあこの理屈をそのままに、変数宣言無しに関数を定義し、即!実行してみましょう。
//これが
var a = function(n) {
...
}
a("引数");
//こうなる
function(n) {
...
}("引数");
しかし、この記事の冒頭に書いてあった内容を思い出してください。「無名関数の場合、行頭にあるとエラーを吐く」という前提があったはずです。
function(n) {
...
}("引数");
//Uncaught SyntaxError: Function statements require a function name
というわけで、この無名関数をエラーの出ない形、つまりfunctionが行頭に無い形に書き換えてしまいましょう。
!function(n) {
...
}("引数")
[function(n) {
...
}][0]("引数");
0 == function(n) {
...
}("引数");
(function(n) {
...
})("引数");
(function(n) {
...
}("引数"));
見覚えのある形・無い形もあるかもしれませんね。しかしこれらは全て即時関数であり、キチンと機能します。もしここにある例よりも奇想天外かつコンパクトな即時関数をご存知の方は、是非!ご教授ください。
ご清覧ありがとうございました!
#おまけ
無名関数でなくとも、関数の定義後即実行は可能です。
function a(n) {
...
}("引数");
a("引数2");
無名関数を使用するのは、大抵は名前空間を汚したくないといった理由かと思います。単に「名前空間なんざ知らない」「関数定義した後にひとまず即実行したい」「一度実行した後も別の場所でこの関数を再利用したい」といった方には、このような書き方もおすすめです。
#おまけ - 2
関数を変数として宣言する際、名前付き関数を入れても問題はありません(ただしその名前はその関数内でしか使えません)。
var a = function b(n) {
if(n < 10) {
return b(n+1);
}
return n;
}(0);
//10
b(0);
//Uncaught ReferenceError: b is not defined