JavaScriptで即時関数を使う理由

  • 365
    いいね
  • 7
    コメント
この記事は最終更新日から1年以上が経過しています。

即時関数とは

即時関数は、関数を定義すると同時に実行するための構文

基本的な使い方

書き方

(function () {
    //処理
}());

引数

引数をもたせられる

(function (param1, param2) {
    //処理
}('hoge', 'fuga'));

返り値

通常の関数同様返り値を持つことができる

var result = (function (param1, param2) {
    return param1 + param2;
}(1, 2));

console.log(result); //3が出力される。

即時関数を使う理由

ズバリ、スコープの汚染を防ぐため。

JavaScriptのスコープ

JavaScriptのスコープは、「グローバルスコープ」と「関数スコープ」のみ。すなわち関数は任意にスコープを作るための唯一の手段である。

関数スコープの中でvarを使って定義された変数は関数の中でローカルな変数になるので、関数の外側の変数を上書きしたりすることはない。

なるべく変数の有効範囲をせばめる

一時変数を使用する一連の処理があった時に、それらの変数をすべてグローバル変数にしてしまうのはアンチパターン。なぜなら、コードが大きくなるにつれグローバルスコープが不要な変数名であっという間に埋め尽くされてしまい、コーディングがしづらくなるだけではなく、予期せぬトラブルの元にもなるから。そのため変数の有効範囲は可能な限りせばめるべき。

一連の処理を即時関数で包み一時的なスコープの中で実行することは、プログラムをスコープ上のサンドボックス内で実行することになるため、スコープの外側へ影響を与えてしまうことを防ぐ。そのため即時関数が必要とされる。

名前付き関数との主な用途の違い

処理が再利用されないのであれば、即時関数を使うべき。。

  • 名前付き関数:再利用を前提とした一連の処理を定義する
  • 即時関数:再利用されない一連の処理を新たなスコープで包み込む

即時関数の使いどころ

1. ページの初期化

ページ読み込み時に時刻を表示する処理。一時変数は初期化完了後にはもはや不要なので、グローバル変数として定義するのは好ましくない。そこで、初期化コード全体を即時関数で包むことで初期化完了後に一時変数などの不要な副産物を残さないようにしている。

init.js
//初期化処理
(function () {
    var label = document.getElementById('date_label'),
        now = new Date();
        label.innerText = now;
    };
}());

2. 機能判定

デバイス判定など。以下の例では処理の中で仕様している一時変数はデバイス判定後は不要なので、グローバル変数として定義したくない。そのため即時関数で包み、実行結果をdeviceという変数に格納するようにしている。

var device = (function() {
    var ua = navigator.userAgent;
    if ((ua.indexOf('iPhone') > 0 && ua.indexOf('iPad') == -1) || ua.indexOf('iPod') > 0 || ua.indexOf('Android') > 0) {
        return 'sp';
    } else {
        return 'pc';
    }
}());

3. プライベートプロパティ/メソッドの定義

JavaScriptにはアクセス修飾子はない。そのためオブジェクト内に定義されたプロパティ/メソッドはすべてパブリックなものとして扱われる。しかし、即時関数やクロージャを利用すればプライベートなプロパティ/メソッドを定義することが可能。

以下は、即時関数とオブジェクトリテラルで作成したもの。これならcountをローカル変数として扱い、オブジェクトの外からのアクセスを防ぐ事ができる。

var counter = (function () {
        //プライベートにしたいプロパティ
        var count = 0;

        return {
            //加算メソッド
            increment: function () {
                count += 1;
                console.log(count);
            }
        };
    }());

counter.increment(); //1が出力される
counter.increment(); //2が出力される

console.log(counter.count); //※undefined