JavaScriptの無名関数を利用した名前衝突の回避

JavaScriptは、言語として名前空間をサポートしておらず、また変数や関数を上書きすることができるため、特に複数人で開発する場合などに名前の衝突が問題になる場合があります。無名関数のローカルスコープを利用することで、変数名や関数名の衝突を効率的に回避することができます。

まず、簡単な例で確認してみます。下記では、グローバル変数と同じ名前の変数を無名関数のローカルスコープで定義して値を代入していますが、出力された値を確認すると、グローバル変数の値は変更されていないことがわかります。また、無名関数内で定義されたローカル変数には、外部からアクセスできないことも確認できます。無名関数は名前を持っていないため、上書きされる心配もなく変数を隠蔽することができています。

JavaScript

var foo = 'foo';

(function() {
  var foo = 'overwrite foo';
  var hoge = 'hoge';
}());

console.log(foo); // 出力:foo
console.log(hoge); // エラー

これを利用して無名関数内にクラスを定義してみます。しかし、無名関数内に定義されたクラスはローカルスコープとなるため、そのままでは外部からアクセスすることができません。そこで、無名関数に引数でwindowオブジェクトを渡し、windowオブジェクトのプロパティにクラスを代入することにより、外部からアクセスできるようになります。

JavaScript

(function(window) {
  function Foo() {
    console.log("constructor");
  }
  Foo.prototype.instanceMethod = function() {
    console.log("instanceMethod");
  };
  Foo.staticMethod = function() {
    console.log("staticMethod");
  };
  window.Foo = Foo;
}(window));

var foo = new Foo();
foo.instanceMethod();
Foo.staticMethod();

/* 出力:
constructor
instanceMethod
staticMethod
*/

JavaScriptでは、関数内でvar宣言を省略した変数はグローバル変数と解釈されるため、上記の例は下記のように書くこともできます。しかし、第3者から見て非常にわかりにくいので、この場合はグローバル変数として予め宣言した方が良いでしょう。

JavaScript

(function() {
  Foo = function() {
    console.log("constructor");
  };
  Foo.prototype.instanceMethod = function() {
    console.log("instanceMethod");
  };
  Foo.staticMethod = function() {
    console.log("staticMethod");
  };
}());

var foo = new Foo();
foo.instanceMethod();
Foo.staticMethod();

/* 出力:
constructor
instanceMethod
staticMethod
*/

また、クラスを代入するwindowオブジェクトのプロパティ名を変更することで、簡単に外部から見たクラス名を変更することができます。下記の例では、無名関数内で定義したFooクラスをwindow.Barに代入することで、外部からはBarクラスとして扱うことができます。そのため、グローバルスコープで別のFooクラスを定義することができています。

JavaScript

(function(window) {
  function Foo() {
    console.log("constructor");
  }
  Foo.prototype.instanceMethod = function() {
    console.log("instanceMethod");
  };
  Foo.staticMethod = function() {
    console.log("staticMethod");
  };
  window.Bar = Foo;
}(window));

var Foo = function() {
  console.log("another Foo");
};

var bar = new Bar();
bar.instanceMethod();
Bar.staticMethod();
var foo = new Foo();

/* 出力:
constructor
instanceMethod
staticMethod
another Foo
*/