『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型
*/
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
プライベート変数は「疑似クラス型」でも利用できます。