この記事の内容
- クロージャについて
- クロージャを用いてJavaScriptでカプセル化を実現する方法
目次
(1) 学習環境の構築と基本的な書き方
(2) 変数とデータ型
(3) 演算子および配列
(4) 制御構文と関数
(5) 関数の応用1
(6) 関数の応用2
(7) オブジェクトの基礎
(8) JavaScriptのオブジェクト指向プログラミング1 概要
(9) JavaScriptのオブジェクト指向プログラミング2 オブジェクトの作り方と継承
(10) JavaScriptのオブジェクト指向プログラミング3 多態性
(11) JavaScriptのオブジェクト指向プログラミング4 カプセル化 <-- この記事の内容
(12) ES6の新機能について
カプセル化について
この記事では、オブジェクト指向プログラミングの3つの特徴である「継承」「多態性」「カプセル化」のうち、「カプセル化(Encapsulation)」について。
※なお、当記事で扱う内容は、カプセル化の中でも具体例のひとつである「データ隠蔽」にスポットをあてて記述している。カプセル化について全て扱っているものではないため、読み手によっては記事と内容に多少の語弊が生じている点、ご容赦いただきたい。
参考:カプセル化(Wikipedia)
https://ja.wikipedia.org/wiki/%E3%82%AB%E3%83%97%E3%82%BB%E3%83%AB%E5%8C%96
ここまでのおさらい的なコードを用意した。
var Dog = function(name, voice) {
this.name = name;
this.voice = voice;
}
var pochi = new Dog("ポチ", "わん!");
console.log(pochi.name); // ポチ と表示される
console.log(pochi.voice); // わん! と表示される
bs11_01.jsのコードはエラーなく実行できる。しかし、Dog
オブジェクトの定義の外から name
や voicd
プロパティにアクセスできてしまう。Javaであれば通常、
class Dog {
private String name;
private String voice;
public String getName() {
return this.name;
}
public String getVoice() {
return this.voice;
}
public void setName(String name) {
this.name = name;
}
public void setVoice(String voice) {
this.voice = voice;
}
}
このように記述して、プロパティは private
をつけて外部から参照できないようにし、代わりに public
のメソッドを用意して参照・上書きできるようにする(ゲッターとセッター)。プロパティに private
をつけて自分自身以外がプロパティに直接アクセスできないようにすることが「カプセル化」である。
JavaScriptには private
という修飾子は用意されていない。そのため、カプセル化を実現するためには、クロージャという仕組みを利用する。
クロージャ
クロージャ(closure)は、「閉鎖」という意味である。以前の記事で触れたように、関数の中に var
をつけて宣言した変数は、その関数の中からしかアクセスできない。
var Sample = function() {
var a = 3;
}
var sample = new Sample();
console.log(sample.a); // 未定義(undefined)と判断される
var a = 3;
と記述した行を this.a = 3;
と書き直すと、最後の console.log
で 3 と表示されてしまう。しかし var a = 3;
としていれば、この変数 a
は外部から参照できないため、 console.log
を実行してもundefined(未定義)と判断される。
この仕組みを利用すれば、 private
の代わりを実現できる。
// Dogのプロパティ名とコンストラクタの引数名は、別のものにする
var Dog = function(name, voice) {
var _name = name;
var _voice = voice;
// ゲッター
this.getName = function() {
return _name;
}
this.getVoice = function() {
return _voice;
}
}
var pochi = new Dog("ポチ", "わん!");
console.log(pochi._name); // undefined になる
console.log(pochi._voice); // undefined になる
console.log(pochi.getName()); // ポチ と表示される
console.log(pochi.getVoice()); // わん! と表示される
ただし、この方法には欠点がある。ゲッターのメソッドの定義を this.getName = function() {...}
としているように、クロージャを使うと、プロトタイプによるメソッドの定義指定ができなくなる。つまり、継承など、プロトタイプを利用したことによるメリットが受けられなくなる。クロージャを使うか、プロトタイプを使うかは、慎重に選択する必要があると思われる。
次回予告
ES6の新機能について(まとめ)