basyura's blog

あしたになったらほんきだす。

インスタンス変数を外から見られないようにしてカプセル化したい

function を new してオブジェクトを生成した際に、外部から変数を見られないようにするにはどうしたらいいのか。
java でいうと下記の当たり前(?)なこと。

public class Person {
  private String name_ = null;
  public Person(String name) {
    name_ = name;
  }
  public String getName() {
    return name_;
  }
  public static void main(String args[]) {
  	System.out.println(new Person("basyura").getName());
    //=> basyura
  }
}

コンストラクタで与えられた name にアクセスできるのは、public な getName メソッド経由のみ。これを javascript でやりたい。

その1 - 素直に書いてみる

var Person = function(name) {
  var name_ = name;
  this.getName = function() {
    return name_
  }
}
var person = new Person('basyura');
print(person.getName());
//=> basyura

インスタンス変数には _ を付けたい派なのだけど、入れ替えなくてもいける。

var Person = function(name) {
  this.getName = function() {
    return name
  }
}
var person = new Person('basyura');
print(person.getName());
//=> basyura

でも、name がインスタンス変数なのかグローバル変数なのか分かりにくい。間違えて使ってしまうこともあるかもしれない。

その2 - prototype で拡張したい場合は?

メモリ効率とかを考えると prototype を使いたいところ。

var Person = function(name) {
  var name_ = name;
}
Person.prototype.getName = function() {
  return name_;
}
var person = new Person('basyura');
print(person.getName());
//=> ReferenceError: name is not defined

getName から name_ は見えない。スコープが違うから。でも prototype 使いたい。

var Person = function(name) {
  this.name = name;
}
Person.prototype.getName = function() {
  return this.name;
}
var person = new Person('basyura');
print(person.getName());
//=> basyura

見えた。けど、外からも見える。

var person = new Person('basyura');
print(person.name);
//=> basyura

その3 - 無理なの?

関数でスコープを作って、その1と2を混ぜる。

var Person = (function() {
  var name_;
  var Constructor = function(name) {
    name_ = name;
  }
  Constructor.prototype.getName = function() {
    return name_;
  }
  return Constructor;
}())

var person = new Person('basyura');
print(person.getName());
//=> basyura
print(person.name);
//=> undefined

できた。インスタンス変数を閉じ込めつつ、prototype による拡張もできる(function スコープ内での定義に限る)。

ねんがんの カプセルか をてにいれたぞ!

そうでもない。

var person1 = new Person('aaaaaaa');
var person2 = new Person('bbbbbbb');
print(person1.getName()); //=> bbbbbbb
print(person2.getName()); //=> bbbbbbb

コンストラクタ関数を定義するときにクロージャを使ってるから同じところを見ちゃうのか・・・な・・・。