クラスとprototypeとプライベート変数

目次

始めに

『JavaScript: The Good Parts ― 「良いパーツ」 によるベストプラクティス』 Douglas Crockford 著, 水野 貴明 訳 - vivid memo を読んで、『Good Parts』を読み直しました。 同著書ではスーパークラスによる継承に触れられていますが、ここでは prototype に注目しています。

(*注釈) 『Good Parts』では3つの型が紹介されていますが、そのままコードを書くと長くなってしまうのでオリジナルの性質は残しつつ、独自にコードを書き直しました。

疑似クラス型

/**
 * 5.1 疑似クラス型 (P53)
 */
function ClassLike (name) {
  this.name = name;
}

ClassLike.prototype.getName = function () {
  return this.name;
};

var classLike = new ClassLike('myName');                  // ClassLike のインスタンスを生成する
alert(classLike.getName());                               // "myName"
alert(classLike.name);                                    // "myName"
alert(classLike.hasOwnProperty('getName'));               // false
alert(classLike.getName === ClassLike.prototype.getName); // true

これは一般的な書き方と思われます。prototypeチェーンによって、classLike.getName を参照しています。

プロトタイプ型

/**
 * 3.5 プロトタイプ (P26)
 */
if (typeof Object.create !== 'function') { // ECMAScript 5 規定の Object.create が未定義の時、Object.create を定義する
  Object.create = function (o) {
    var F = function () { ; };
    F.prototype = o;
    return new F();
  };
}

/**
 * 5.3 プロトタイプ型 (P58)
 */
var MyPrototype = {
  getName: function () {
    return this.name;
  }
};

var myPrototype = Object.create(MyPrototype);       // myPrototype.prototype = MyPrototype;
myPrototype.name = 'myName';
alert(myPrototype.getName());                       // "myName"
alert(myPrototype.name);                            // "myName"
alert(myPrototype.hasOwnProperty('getName'));       // false
alert(myPrototype.getName === MyPrototype.getName); // true

ECMAScript 5 で定義された Object.create を利用した手法です。prototypeチェーンを利用する性質は「疑似クラス型」と同じです。

関数型

/**
 * 5.4 関数型 (P59)
 */
function myFunction (name) { // 仮引数 name はプライベート変数となる
  var that = {};

  that.getName = function () {
    return name;
  };

  return that;
}

var myFn = myFunction('myName');
alert(myFn.getName());                 // "myName"
alert(myFn.name);                      // undefined
alert(myFn.hasOwnProperty('getName')); // true

クロージャを利用して外部から参照できないプライベート変数を定義しています。prototypeチェーンは利用していません。 『Good Parts』著者の Crockford さんは変数(name) を隠蔽できる性質から「関数型」を強くお勧めしています。

疑似クラス getter, setter型

/**
 * 疑似クラス getter, setter型
 */
function ClassLike2 (name) {                         // 仮引数 name はプライベート変数となる
  this.getName = function getName () {
    return name;
  };

  this.setName = function setName (inputName) {
    name = String(inputName);
  };
}


/**
 * サブクラス
 */
function ClassLike2Sub (message) {                   // 仮引数 message はプライベート変数となる
  this.getMessage = function getMessage () {
    return message;
  };

  this.setMessage = function setMessage (inputMessage) {
    message = String(inputMessage);
  };
}

ClassLike2Sub.prototype = new ClassLike2('myName2'); // ClassLike2 を継承する


/**
 * 検証
 */
var classLike2, classLike2Sub;

classLike2 = new ClassLike2('myName1');
alert(classLike2.getName());                         // "myName1"
alert(classLike2.name);                              // undefined
alert(classLike2.hasOwnProperty('getName'));         // true

classLike2Sub = new ClassLike2Sub('myMessage1');
alert(classLike2.message);                           // undefined
alert(classLike2Sub.getMessage());                   // "myMessage1"
classLike2Sub.setMessage('myMessage2');
alert(classLike2Sub.getMessage());                   // "myMessage2"

alert(classLike2Sub.hasOwnProperty('getMessage'));   // true
alert(classLike2.name);                              // undefined
alert(classLike2Sub.getName());                      // "myName2"
classLike2Sub.setName('myName3');
alert(classLike2Sub.getName());                      // "myName3"
alert(classLike2Sub.hasOwnProperty('getName'));      // false

プライベート変数は「疑似クラス型」でも利用できます。

参考URL