Skip to content

Instantly share code, notes, and snippets.

@cou929
Created May 12, 2010 22:47
Show Gist options
  • Save cou929/399231 to your computer and use it in GitHub Desktop.
Save cou929/399231 to your computer and use it in GitHub Desktop.
グローバル汚染を避ける方法(ブロックスコープ)について
グローバル変数を避けるには単純な命名規則を用いるか、スコープ変数を利用するかどちらかが可能。
スコープは関数による関数スコープ、with 文によるスコープ、let によるブロックスコープがある。
1. _ で始まるオブジェクトプロパティを利用する
利点: 単純。シンプル。簡単。
欠点: 内部のみで利用する変数や関数に命名規則が必要。そのアクセス時は this を付ける必要がある。特にイベントハンドラや setTimeout, Array.filter などに渡すコールバック関数の内部では this が切り替わるため不便。
また、変数やメソッドの定義末尾の , を書き忘れて構文エラーによくなる。
var global = {
// 内部でのみ利用する変数や関数を定義
_local = "foo",
_func = function() { ... },
get _item() {
delete this._item;
return this._item = /* 重たい処理 */ ;
},
...
prop: "bar",
method: function() {
alert(this._local); // this 経由の参照が必要
alert(this._item);
alert(prop);
this._func(); // this 経由の参照が必要
...
}
}
2. クロージャを利用するため関数式を用いて隠蔽する場合
利点: 比較的一般的に利用されている書きやすく読みやすい方法。this を付ける必要がなくなったし、コールバック関数内でも this の変化を気にせずグローバル変数やグローバル関数にアクセスするつもりで使える。
欠点: 全体が1つの関数になるため、デバッグするときに各メソッドに対してブレークポイントを設定することができない。これは大規模開発になると致命的。
また、クロージャ変数を var で定義するため、内部変数で getter を使った遅延初期化をするときには何か適当なオブジェクトに入れてプロパティアクセスさせる必要がある。
var global = (function() {
// 内部でのみ利用する変数や関数を定義
var local = "foo";
var func = function () { ... }
var obj = {
get item() {
delete this.item;
return this.item = /* 重たい処理 */ ;
}
...
}
...
var global = { // クロージャ変数
prop: "bar",
method: function() {
alert(local); // this を書く必要なし、コールバック関数内で特に助かる
alert(obj.item); // 遅延初期化する変数はプロパティアクセスとして書く
alert(prop);
func(); // 関数も this なしで呼び出せる
...
},
...
}
return global; // クロージャ変数を返してグローバル変数に代入させる
})()
3. クロージャを利用するため new コンストラクタを用いる場合
利点: 書きやすく読みやすい方法。利点は先ほどとほぼ同じ。
欠点: 欠点も先ほどと同じ。局所的に使うには最適だが全体に使うには厳しい。
var global;
new function() {
var local = "foo";
var func = function () { ... }
var obj = {
get item() {
delete this.item;
return this.item = /* 重たい処理 */ ;
}
...
}
...
global = { // クロージャ変数
prop: "bar",
method: function() {
alert(local); // this を書く必要なし、コールバック関数内で特に助かる
alert(obj.item); // 遅延初期化する変数はプロパティアクセスとして書く
alert(prop);
func(); // 関数も this なしで呼び出せる
...
},
...
}
};
4. with 文と anonymous オブジェクトを利用する場合
利点: 内部利用の変数、関数を定義する時以外は this を気にする必要がなく比較的読み書きしやすいし、デバッグ時に各メソッド毎にブレークポイントを設定できるため大規模開発にも対応可能。
仕様的には JS 1.5 でも対応可能なため WebDev でも利用できるテクニック。
遅延初期化を行う場合も読み書きしやすい。
欠点: 内部利用の変数や関数の定義が複雑化するとそこで this の扱いが面倒になる。内部変数名の指定と初期化を分離して書く場合は this を気にする必要なくなるが、変数定義が2カ所に分割するのは管理がしにくい。
with({ // 内部変数はこのオブジェクトのプロパティに設定する
locale: "foo",
func: function() { ... },
get item() {
delete this.item;
return this.item = /* 重たい処理 */ ;
},
...
func2: function() {
alert(this.local); // 無名オブジェクトの定義内部では this 参照が必要になる
this.func(); // 関数呼び出しも勿論同じ
},
func3: null, // 名前だけ確保して後で定義する場合
...
}) {
func3 = function() {
alert(local); // ここで初期化するなら this 参照不要
this.func();
}
var global = { // グローバル変数定義
prop: "bar",
method: function() {
alert(local); // 本体部分では this を気にしなくて良い
alert(item); // 遅延初期化する変数へのアクセスも簡単
alert(prop);
func();
func2();
...
},
...
}
}
6. with 文と自身を返すメソッドを持つ anonymous オブジェクトを利用する場合
利点: 広く使える this 気にしないで済む方法。
欠点: 内部変数宣言の仕方がやや特殊になる。let の代わりに scope(). と書くだけだと思えるなら問題ない。
遅延初期化はプロパティアクセスさせる(あるいは __defineGetter__ 使う)必要がある。
with({ // 内部変数はこのオブジェクトのプロパティに設定する
let: function(name, val) { this[name] = val; },
scope: function() { return this; }
}) {
scope().locale: "foo"; // scope() でスコープオブジェクト取得
let("local2", "foo2"); // let 宣言みたいな書き方もできるが面倒
scope().func: function() { ... };
scope().obj = {
get item() {
delete this.item;
return this.item = /* 重たい処理 */ ;
}
...
}
scope().__defineGetter__("item2", function() {
delete this.item2;
return this.item2 = /* 重たい処理 */ ;
});
...
scope().func2 = function() {
alert(local); // ここで初期化するなら this 参照不要
this.func();
}
...
var global = { // グローバル変数定義
prop: "bar",
method: function() {
alert(local); // 本体部分では this を気にしなくて良い
alert(item); // 遅延初期化する変数へのアクセスも簡単
alert(prop);
func();
func2();
...
},
...
}
}
5. let によるブロックスコープを利用する
利点: 単純明快。
欠点: let が使える環境 (Firefox 2 以降) に限定されてしまう。
遅延初期化はプロパティアクセスさせる必要がある。
{
let local = "foo";
let func = function() { ... }
let obj = { // 遅延初期化するものはプロパティに
get item() {
delete this.item;
return this.item = /* 重たい処理 */ ;
}
...
};
var global = {
prop: "bar",
method: function() {
alert(local);
alert(obj.item); // 遅延初期化する変数へのアクセス
alert(prop);
func();
...
},
...
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment