ふと思ったのですが、
JavaScriptのクラスの書き方が
自分の場合マチマチで統一感が無いなと(^_^;)

ライブラリのクラスを継承する場合は、
その流儀に倣った方が良いかなと思うので、
その影響もあるかもしれません。

ときに、
JavaScriptはクラス・ベースでは無く
プロトタイプ・ベースな言語なので、
「クラス」という言い方は厳密には正しくないのかもしれませんが・・・。

それはともかく、
どんな書き方をするべきなのか気になったので、
ググってみたらこんな感じに色々ありますね。

自分は、
プロトタイプに追加な書き方、

var A = function() {
	this.property = 'something';
};
A.prototype.method = function() {
};

プロトタイプを上書きな書き方、

var A = function() {
	this.property = 'something';
};
A.prototype = {
	constructor: A,
	method: function() {
	}
};

でやってたりすることが多いようです。

また、
enchant.jsライブラリを利用している場合は、
こちらに示されているように、
以下のようにスマートな感じに書くことができます。

enchant();
var A = Class.create({
	initialize: function(){
		this.property = 'something';
	},
	method: function(){
	}
});

ただ、これらの書き方だと
privateな変数や関数はどうしたものかという問題があります。
個々のメソッドをクロージャにすれば可能ではあるけど、
インスタンスで共有なprivateなのは作れないですよね。
そんな時は以下のようにアンダースコアな名前にして、

this._privateProperty = 'secret!?';

privateを装ったりするわけですが、
実際は丸見えなpublicなわけです。
自分はあんま気にせずやったりしますが・・・(^_^;)

privateなのを作りたい時のクラスの書き方はどうすべきなんだろうと、
調べてみたらこちらを見つけました。
クロージャー・パターンとか言うみたいです。

var A = function() {
	var privateVariable = 'private',
		privateFunction = function() {
		};
	this.publicProperty = 'public';
	this.publicMethod = function() {
	};
};

上記の書き方だと、
プロトタイプな書き方に比べて、
どんなプロパティやメソッドがあるのかが、
ぱっと見でちょっと分かりにくい感があるけど、
なるほど悪くないなと思いました。

例えば、
コンストラクタがパラメータを持ち、
そのパラメータをメソッドが参照するような場合、
プロトタイプな書き方だと以下のようになりますが、

var A = function(a, b, c) {
	this.a = a;
	this.b = b;
	this.c = c;
};
A.prototype.method = function() {
	someFunc(this.a, this.b, this.c);
};

クロージャー・パターンだと以下のようになります。
コンストラクタのパラメータをプロパティとして持たなくてもよいので、
スッキリと書けますね。
これはイイかも。

var A = function(a, b, c) {
	this.method = function() {
		someFunc(a, b, c);
	};
};

さらに、
全インスタンスに共有な関数や変数まで考慮すると
以下のように書けそうですね。
無名の即時実行関数を利用してクロージャ化します。

var A, B;
(function() {

var privateSharedVariable = 'privateShared',
	privateSharedFunction = function() {
	};

A = function() {
	var privateVariable = 'private',
		privateFunction = function() {
		};
	this.publicProperty = 'public';
	this.publicMethod = function() {
	};
};
A.publicSharedVariable = 'publicShared';
A.publicSharedFunction = function() {
};

B = function() {
	var privateVariable = 'private',
		privateFunction = function() {
		};
	this.publicProperty = 'public';
	this.publicMethod = function() {
	};
};
B.publicSharedVariable = 'publicShared';
B.publicSharedFunction = function() {
};

}());

続いて、
JavaScriptのクラス継承の書き方について考えてみます。

こちらが参考になりますね。
こちらのように継承用の関数を使うやり方もありますが、
独立して完結する以下の様な書き方が個人的には好みな感じです。

var Base = function(a, b, c) {
};
var Extend = function(a, b, c) {
	Base.call(this, a, b, c); // あるいは Base.apply(this, arguments);
}
Extend.prototype = Object.create(Base.prototype);
Extend.prototype.constructor = Extend;

ちなみに、

Extend.prototype.constructor = Extend;

をやらなくても実用上は問題なかったりします。
というのは、
new 演算子は以下の様な感じになっていて、
prototype.constructorを呼び出しているわけではないようなので。
(間違ってたらすいません)

var a = new A(arg1, arg2, arg3);
 ↓
var a = {};
A.call(a, arg1, arg2, arg3);

とは言え、省かずにちゃんと書いておいた方が無難だと思います。
例えば、

if ( object.constructor === SomeClass ) {
 .
 .
}

のように、特定クラスの識別とかで利用している場合があるかもしれないので。

通常は、継承クラスのコンストラクタ内で
スーパークラスのコンストラクタを呼び出すわけですが、
以下のようにメソッド内に書くことも可能だったりします。
クラス・ベースな言語には真似できない芸当ですね(^_^;)

var Base = function(a, b, c) {
};
var Extend = function() {
	this.create = function(a, b, c) {
		Base.call(this, a, b, c);
	};
}
Extend.prototype = Object.create(Base.prototype);
Extend.prototype.constructor = Extend;

上記のようなのはあまり無いと思うのですが、
スーパークラスのコンストラクタがパラメータを要求する場合に、
そのパラメータがすぐに確定できなかった時とかで使えるかもしれません。

JavaScriptにおける「クラス継承」と言うのは、
他クラス(というか他オブジェクト)の実装を
自オブジェクトに導入するという感覚なんだと思います。

ところで、
今回こちらのページを見て気になったことがありました。
それはコンストラクタで

return this;

とかやっていることです。
なんじゃこれは? 必要なのか? 
と思ってググってみたら、
こちらの解説を見つけました。
いや~、これは知りませんでした。

要するに、
コンストラクタがオブジェクトを返した場合はそれがインスタンスと成る
ということのようです。
new は暗黙的に return this; してたんですね。

JavaScriptはなかなか奥が深いです(^_^;)

★追記 さらに考察してみました。

コメントする

メールアドレスが公開されることはありません。が付いている欄は必須項目です。