通常、オブジェクトのプロパティには、どこからでもアクセスできる。
このままだと、状態管理が大変になってしまう。
管理が煩雑になるし、予期していない箇所で値が書き換えられる恐れがあり、バグの温床にもなる。
特定の方法でのみアクセスできる変数を作ることで、そういった状況を防ぐことが出来る。
このような変数は、プライベート変数、などと呼ばれる。
モジュールパターン
モジュールパターンと呼ばれる方法で、プライベート変数を実現できる。
具体的には、即時実行関数を利用し、クロージャを活用することで、実現する。
クロージャについては、改めて別の記事で書く予定。
var obj1 = {data:7}; // obj1.dataには、いつでも、どこからでもアクセスできてしまう obj1.data = 99; console.log(obj1.data); // 99 var obj2 = function(){ var data = 7; return { getData: function(){ return data; }, incData: function(){ data++ } }; }(); console.log(obj2.getData()); // 7 obj2.data = 99; console.log(obj2.getData()); // 7 obj2.incData(); console.log(obj2.getData()); // 8 // dataは、即時実行した無名関数のローカル変数なので、外部からはアクセスできない。 // アクセス出来るのは、dataと同じスコープで作成されたgetDataとincDataのみ // 無名関数であるため既に破棄されており、アクセス方法が追加されることもない // アクセス出来るのは恒久的に、getDataとincDataのみ console.log(obj2.data); // 99
これで、プライベート変数であるdata
を作れた。
が、上記の例では、それとは別のobj2.data
が作成されていることに留意する必要がある。
obj2.data = 99;
を実行した際に、obj2.data
は存在しなかったため、99
を格納するプロパティとして、新たに作成された。
ローカル変数data
と、obj2
のプロパティであるdata
は、名前が同じであるだけで、全くの別物である。
コンストラクタでのプライベート変数
上記の仕組みを即時実行関数ではなくコンストラクタに実装することで、インスタンス毎にプライベート変数を持つことが出来る。
function MyConstructor(num){ var data = num; return { getData: function(){ return data; }, incData: function(){ data++ } }; }; var obj1 = MyConstructor(10); var obj2 = MyConstructor(70); // 外部からはdataにアクセスできない console.log(obj1.data); // undefined console.log(obj2.data); // undefined obj1.incData(); console.log(obj1.getData()); // 11 console.log(obj2.getData()); // 70
上記の例では、インスタンスが作成される度に、各インスタンスがそれぞれに、プライベート変数data
と、それにアクセスするためのメソッドを持つ。
プライベート変数の共有
先ほどの例では、インスタンス毎にプライベート変数を持っていた。
だが、即時実行関数と組み合わせることで、全てのインスタンスが同一のプライベート変数を持つようにすることも出来る。
var Student = function(){ var grade = 1; function InnerStudent(name){ this.name = name; }; InnerStudent.prototype.getGrade = function(){ return grade; }; InnerStudent.prototype.incGrade = function(){ grade++ }; return InnerStudent; }(); var student1 = new Student('Tom'); var student2 = new Student('Bob'); console.log(student1.name); // Tom console.log(student2.name); // Bob student1.incGrade(); student2.incGrade(); // gradeは全てのインスタンスからアクセス出来ることが分かる console.log(student1.getGrade()); // 3 console.log(student2.getGrade()); // 3 // 外部からはgradeにアクセスできない console.log(student1.grade); // undefined
上記の例では、即時実行関数により、Student
には、InnerStudent
が入っている。
InnerStudent
は、自身のプロパティであるname
を持つほか、プロトタイプとして、getGrade()
とincGrade()
を持つ。この2つのメソッドは、即時実行された無名関数のローカル変数でるgrade
にアクセスしている。
プロトタイプであるため、全てのインスタンスはこの2つのメソッドを利用でき、それにより、どのインスタンスからでもgrade
という同一のプライベート変数にアクセスできる。