PHP/Elixirエンジニアのブログ

有益なアウトプットを心がけます。

即時関数ってなに?

即時関数って定義時に即座に実行される関数ってのはわかるけど、イマイチ使い所がわからなかったので、調べてみました。
案外コードみてると出没するので覚えておくべきと思い纏めます。

 STEP1 即時関数の構文

構文は2パターンあります。

// パターン1
(function () {
  // 関数の中身
}());

// パターン2
(function () {
  // 関数の中身
})();

異なる部分は、最後の括弧です。
即時関数に引数を渡す場合は以下のようにします。

// パターン1
(function () {
  // 関数の中身
}('arg1', 'arg2'));

// パターン2
(function () {
  // 関数の中身
})('arg1', 'arg2');

即時関数は通常の関数のように戻り値を指定することも可能です。

var result = (function (weight, height) {
  return weight / (height * height)
}(50, 1.5));

console.log("bmi is " + result);  // bmi is 22.2....

 STEP2 即時関数の用途

 定義後に実行され再利用されない場合

例えば、画面遷移時のページの初期化や、ブラウザのバージョンの判定等々です。

var VENDER = (function () {
  var ua = navigator.userAgent;
  if (ua.indexOf('Webkit') != -1) {
    return 'webkit';
  } else {
    return '';
  }
}());

判定後にグローバルスコープに余計な関数名が残ることがありません。
また、最初の一度のみ実行されれば良いので即時関数に向いています。再利用もしません。

 スコープを汚染せず新たなスコープを作成する

何を言っているんだっと思う人の為にコードの例で説明します。

例:即時関数を使わない場合(アンチパターン)
var bmi = 0;

var weight = 50;
var height = 1.5;

bmi = weight / (height * height);
console.log("bmi is " + bmi);  // bmi is 22.2....

上記のコードはうまく動きます。しかし、変数weight,heightをグローバルスコープに定義しています。もし上記コードより前に同じ変数名が定義された場合、変数を上書きしてしまうので、予期せぬ動きをする可能性があります。このようなコードを書くとコードが大きくなるにつれてグローバルスコープが不要な変数で埋め尽くされてしまいトラブルの元になります。

では、一時的な変数を関数の中に定義してローカル変数にしてしまいます。

例2:名前付き関数を使う場合(アンチパターン)
var result = 0;
var bmi = function() {
  var weight = 50;
  var height = 1.5;
  return weight / (height * height); 
}
result = bmi();
console.log("bmi is " + result);  // bmi is 22.2....

これにより、変数weightheightが関数bmiのローカル変数となるのでグローバルスコープを汚染しなくなりました。また、weightheightが外側のスコープから上書きされることはなくなり安全になりました。しかし、ここでは関数を格納しているbmiという変数がグローバルスコープに定義されるため、一番目のアンチパターンと同じ問題が発生します。
まだ、一番のパターンよりはマシなコードですが、このbmi関数が再利用されない関数だとしたら、グローバルスコープに余計な変数を残す必要はないので、次のように即時関数を使用します。

例3:即時関数を使う場合
var result = 0;
result = (function() {
  var weight = 50;
  var height = 1.5;
  return weight / (height * height); 
}());
console.log("bmi is " + bmi);  // bmi is 22.2....

これで、例2のようにbmiという関数はグローバルに生成されません。
しかし、resultという変数をグローバル変数に宣言しているので、全く汚染しなようにするには以下のようにします。

例4:即時関数を使う場合
(function () {

    var bmi = function() {
      var weight = 50;
      var height = 1.5;
      return weight / (height * height); 
    };
    console.log("bmi is " + bmi());  // bmi is 22.2....

}());

と以上になりますが、即時関数は以上のようにグローバルスコープを汚染しないように使用することが多いようです。試しに適当にライブラリの中身を見てみると以外に即時関数が出てくるのでチェックして見てください。