私の歴史と今

振り返ると恥ずかしくなるのが私の歴史。だけどそのときは真面目に書いていた訳でね。そんな今の私を書いていく。

JavaScriptの継承とは

次の2つの継承がある。

    • クラスベースの継承
    • プロトタイプベースの継承

Javaを知っている人なら、クラスベースの継承に馴染みがあるはず。

Javaの場合

public class Super{
	public void print1(){
		System.out.println("super1");
	}
	public void print2(){
		System.out.println("super2");
	}
}
public class Sub extends Super{
	public void print2(){
		System.out.println("sub2");
	}
}
public class Main {
	public static void main(String[] args){
		Super sup = new Super();
		Sub sub = new Sub();
		sup.print1();	// "super1"と標準出力
		sub.print1();	// "super1"と標準出力
		sup.print2();	// "super2"と標準出力
		sub.print2();	// "sub2"と標準出力
	}
}

SubクラスはSuperクラスを継承しているので、Subクラスのオブジェクトに対してもprint1メソッドを呼び出せる。
また、SubクラスはSuperクラスのprint1メソッドをオーバーライドしているので、Subクラスのオブジェクトに対してprint2メソッドを呼び出すと、Superクラスのprint2メソッドではなく、Subクラスに定義したprint2が実行される。

JavaScriptの場合

次のようにすれば、SubクラスはSuperクラスのプロパティを継承できる。

// スーパークラスの定義
var Super = function(){
	this.name = 'super';
	this.showName = function(){
		alert(this.name);
	};
}
// サブクラスの定義(空っぽ)
var Sub = function(){
}
// SubクラスのprototypeプロパティにSuperクラスのインスタンスを設定する 
Sub.prototype = new Super();

Superクラスのプロパティを継承していることを確かめる。

// Subクラスのプロパティには直接何も設定していないがSuperクラスのメソッドを呼び出せる。
var sub = new Sub();
sub.showName();	// "super"と表示

継承しているので、instanceof演算子の評価結果はSuperに対しても「true」となる。もちろんObjectクラスはすべてのクラスが継承しているのでObjectに対しても「true」となる。

// 勿論、すべてtrue
alert(sub instanceof Object); // "true"
alert(sub instanceof Super);  // "true"
alert(sub instanceof Sub);    // "true"

subに対してshowName()メソッドを呼び出せるからといって、subがshowName()メソッドを直接所有している訳ではない。プロトタイプオブジェクトをいじれば挙動が変わる。

Sub.prototype.name='super(chenged)';
sub.showName();	// "super(chenged)"と表示

それはhasOwnPropertyメソッドでも確かめられる。

alert(sub.hasOwnProperty('name')); // "false"と表示

つまり、sub.showName()メソッドが呼ばれてはいるが、sub.prototype.showName()メソッドが実行されている。
だからこそ、変数subが参照するオブジェクトに同名のプロパティを設定して、継承プロパティをオーバーライドすることができる。

sub.name='sub';
sub.showName();	// "sub"と表示される。

hasOwnProperty()メソッドでも、オーバーライドしていることがわかる。

alert(sub.hasOwnProperty('name')); // "true"と表示

ということは、独自プロパティを削除すれば、元に戻る。「undefined」になることはない。

// Subクラスに直接設定したプロパティを削除すれば元に戻る。
delete sub.name;
sub.showName();	// "super(changed)"と表示

まとめ

    • JavaScriptはプロトタイプベースの継承をサポートしている。
    • コンストラクタ関数.prototypeプロパティに設定したオブジェクトを継承できる。
    • 継承先にプロパティをコピーするのではなく、継承先がそのプロパティを所有しているように見せかけるだけ。
    • プロトタイプのプロパティ値を変更すると、それを継承しているオブジェクトは影響を受ける。