せめてこれくらいできないと、Strategy パターンとか書けませんので。 というわけで、JavaScript でクラス的な発想の継承を行うための書き方について考えていました。条件として:
といったところです。 // 最初の「クラス」。これは普通 function Foo () { this.inheritance_depth = 1; } Foo.prototype.get_name = function() { return ("Foo"); }; Foo.prototype.get_inheritance_stack = function() { return (["Foo"]); }; // 2 段目までの、簡易な継承。これも比較的普通 with (Bar = function () {
Super = Foo; prototype = Object.create(Super.prototype); prototype.Super = Super; // ← 継承階層内で一度しか使えない prototype.get_name = function() { return ("Bar"); }; prototype.get_inheritance_stack = function() { return (this.Super.prototype.get_inheritance_stack.apply(this). concat("Bar") ) }; } // 3 段目以上の継承 (Object を基底に 1 段目からこの形でも構わない) Baz = (function () { // 基底クラス var Super = Bar; // コンストラクタ var Class = function () { // コンストラクタ・チェーン Super.apply(this); this.inheritance_depth ++; } // プロトタイプのコピー Class.prototype = Object.create(Super.prototype); // オーバーライドされるメソッド Class.prototype.get_name = function() { return ("Baz"); }; // 継承元への連鎖呼び出しは、クロージャへ基底クラスをバインドして Class.prototype.get_inheritance_stack = function() { return (Super.prototype.get_inheritance_stack.apply(this). concat("Baz") ); }; return Class; })(); // ------------------------------------------------------------------- baz = new Baz(); alert( "Name: " + baz.get_name() + " | " + // → "Baz" "Depth: " + baz.inheritance_depth + " | " + // → 3 "Stack: " + baz.get_inheritance_stack() ); // → ["Foo", "Bar", "Baz"] baz.constructor が "Class" になる。なぜだろう? コンストラクタのチェーンやメソッドのチェーンが不要、あるいは少々冗長な書き方を許容できるのであれば、話は簡単なんですけれどもね。 function Foo () { this.inheritance_depth = 1; } Foo.prototype.get_name = function() { return ("Foo"); } Foo.prototype.get_inheritance_stack = function() { return (["Foo"]); } function Bar () { Foo.apply(this); this.inheritance_depth ++; } Bar.prototype = Object.create(Foo.prototype); Bar.prototype.constructor = Bar; Bar.prototype.get_name = function() { return ("Bar"); } Bar.prototype.get_inheritance_stack = function() { return (Foo.prototype.get_inheritance_stack.apply(this). concat("Bar") ); } こう冗長だと、リファクタリング支援のツールは要りそうだな。 あるいは、もっとノホホンとダックタイピングしていれば良いのか。 参考: Object.create() は、ECMA の 5 から。「new 方式」のデメリットは? → 継承の際に基底クラスのコンストラクタが走った結果の「インスタンス」(オブジェクト?)をプロトタイプとしてしまうので、その時点で余計なインスタンスの設定等の副作用が出てしまうこと。なので、純粋にプロトタイプだけをチェーンした、空のオブジェクトが欲しいのです。 "prototype" は、「クラス」(コンストラクタ)のプロパティであり、そこから生成された「インスタンス」の "__proto__" プロパティが指す先となる。よって、実際にチェーンするのは「prototype」ではなく「__proto__」。 「__proto__」を使っても構わなければ継承先のコード内に継承元の名前は出なくなるのでスッキリするのですが、実装依存してしまいますね。ECMA 6 から入るようです。 ∥ DailyJS: JS101: __proto__ http://dailyjs.com/2012/11/26/js101-proto/ , ECMA の 5 は 2009 年、ECMA の 6 は策定中。 ∥ ECMAScript - Wikipedia function Foo () { あとは mixin か。 |