名前空間のイロハ

名前空間は、中規模以上のアプリを書くにあたって必須のパターンです。 JavaScriptでは、グローバルスコープを汚染することを防ぐ代わりに、 唯一のアプリケーションのグローバルオブジェクトを作成することが主流です。

また、プロトタイプチェーンを用いることによって、 モジュールコンテナを作成することもできます。


var MYAPP = {};

MYAPP.name = "My First SPA";
MYAPP.data = "2015/06/25";

MYAPP.Update = function(){
  ...
};
MYAPP.Delete = function(){
  ...
};

// オブジェクトのコンテナ
MYAPP.modules = {};

MYAPP.modules.name = "My First SPA's first module";
MYAPP.modules.data = {
  1 : "test",
  2 : "test2",
  3 : "test3"
};

ベストプラクティス:名前空間

なお、名前空間を作成する際には、サードパーティプラグインの読み込みや 逆にプラグインとして公開して使ってもらう場合に、この唯一のグローバルオブジェクトが 競合してしまう可能性がないとも言い切れません。

したがって、以下のような定義をします。


if (typeof MYAPP === "undefined"){
  var MYAPP = {};
}

そして、これをもっと短くした以下の書き方もあります。


var MYAPP = MYAPP || {};
var MYAPP.module = MYAPP.module || {};

ベストプラクティス:『JavaScript パターン』における名前空間

JavaScriptが提供している名前空間のメソッドなどはないため、 おのおのが自分の最善と思える名前空間の定義をしており、 先人の方々の例が「JavaScript 名前空間」などとググるとたくさん 参考にできます。

『JavaScriptパターン』(O'Reilly) のp91-92にあるnamespace()メソッドは非常に示唆に富んでいるので、ここで紹介 したいとおもいます。作者のStoyan Stefanovさんの説明は非常にわかりやすいので、ここでわからなかったらぜひ原著を見てみてください。

まず、いかのように名前空間を準備したいとします。


var MYAPP = {
  modules: {
    moduleA: {},
    moduleB: {}
  }
};

これを、たとえば以下のようなnamespace()メソッドで定義できるようにしましょう。


MYAPP.namespace('MYAPP.modules.moduleA');
MYAPP.namespace('MYAPP.modules.moduleB');

また、ここで紹介されているメソッドは、以下のような宣言方法にも対応しています。


// 戻り値をローカル変数に代入する
var module2 = MYAPP.namespace('MYAPP.modules.module2');
module2 === MYAPP.modules.module2; // true

// 先頭のMYAPPを省略
MYAPP.namespace('modules.module51');

// 長い名前空間
MYAPP.namespace('once.upon.a.time.there.was.this.long.nested.property');

では、実装の中身を見ていきましょう。


var MYAPP = MYAPP || {};

MYAPP.namespace = function(ns_string){
  var parts = ns_string.split('.'), // . で区切った配列
      parent = MYAPP, // グローバルオブジェクトのアプリ名
      i;

  // 先頭のグローバルを取り除く
  if ( parts[0] === "MYAPP"){
    parts = parts.slice(1); // 先頭を削除
  }

  for ( i = 0; i < parts.length; i += 1){
    // プロパティが存在しなければ作成する
    if ( typeof parent[parts[i]] === "undefined"){
      parent[parts[i]] = {}; // モジュールのオブジェクト生成
    }
    parent = parent[parts[i]];
  }
};