Hatena::ブログ(Diary)

tacamy memo (旧) このページをアンテナに追加 RSSフィード

2012年12月09日

Javaと比較しつつ、JavaScriptのプロトタイプについて調べてみる

オブジェクトだらけで分かりにくいので、ここではnew演算子で生成したオブジェクトのことをインスタンスと呼ぶことにする。

オブジェクトの生成

Java
  • クラスという設計図をインスタンス化することでオブジェクトを生成する
  • クラスベースのオブジェクト指向
JavaScript
  • クラスという設計図は存在せず、オブジェクトからオブジェクトを生成する
  • プロトタイプベースのオブジェクト指向 (プロトタイプとは「より縛りの弱いクラス」のようなもの)

コンストラクタ

  • コンストラクタの役割はこれから生成するインスタンスの初期化をすること
  • new演算子でインスタンスを生成するときにコンストラクタの中身が実行される
  • コンストラクタに戻り値は持てない
Java
  • クラス (設計図) の中に、クラスと同じ名前でコンストラクタを記述する
  • コンストラクタがなければ、引数なしの空のコンストラクタが自動的に生成される
  • クラスからインスタンスを生成するときにコンストラクタの中身が実行される
// クラスを作成
class ClassName {
	--- メンバ ---
	ClassName() { // これがコンストラクタ
		--- 初期化処理 ---
	}
	--- メソッド ---
}
// ClassName型のインスタンスを生成 (ClassName()でインスタンスを実行)
ClassName obj = new ClassName();
JavaScript
  • 関数の中身がそのまま全てコンストラクタになる
  • 同じコンストラクタを使って生成されたインスタンスは、同じプロパティ (変数・メソッド) を持つことになる
  • 生成したインスタンスに追加したプロパティは、そのインスタンス内のみで有効
  • コンストラクタのプロパティはインスタンスを生成するたびにメモリを確保する
// コンストラクタにする関数を作成
function Constructor() {
	--- 初期化処理 ---
}
// コンストラクタのプロパティを持ったobjインスタンスを生成
var obj = new Constructor();

prototypeプロパティと暗黙の参照

prototypeプロパティ
  • JavaScriptの全ての関数オブジェクトは、prototypeプロパティを持っている
  • プロトタイプの中身は、そのオブジェクトを基に生成されたインスタンスから参照される (暗黙の参照)
// コンストラクタになる関数を作成
function Constructor() {}
// コンストラクタのprototypeプロパティに、aという変数を作成して10を代入
Constructor.prototype.a = 10;
// コンストラクタを使ってインスタンスを生成
var obj = new Constructor();
// コンストラクタのprototypeプロパティの中身を参照
console.log(obj.a); // 10
複数オブジェクト間でのプロパティの共有
  • 同じオブジェクトを基に複数のインスタンスを生成した場合、どのインスタンスからもプロトタイプの中身 (プロパティ) を参照できる
  • その場合、複数のインスタンスから1つのプロパティを参照しているだけなので、メモリの消費は1つ分だけで済む
  • インスタンスからプロトタイプのプロパティ値を変更すると、別のメモリ領域にそのインスタンス自身のプロパティとして新たに定義されるので、元のオブジェクトのプロトタイプの中身には影響を与えない
暗黙の参照をJavaで例えると
  • メンバの隠蔽に似てるかもしれない
  • スーパークラスでメンバを定義し、サブクラスでメンバを上書き定義すると、自分自身 (サブクラス) のメンバを優先してアクセスするので、スーパークラスのメンバが見えなくなることをメンバの隠蔽という
  • 隠蔽した場合でも、スーパークラス型でサブクラスをインスタンス化すれば、スーパークラスのメンバにアクセスする (JavaScriptだと上書きしたプロパティをdeleteするか、値をundefinedにするかみたいな話?)
// スーパークラスでメンバを定義
class A {
	int a = 10;
}
// サブクラスBでAを継承しつつ、メンバを上書き
class B extends A {
	int a = 20;
}
// サブクラスCでAを継承
class C extends A {}
// インスタンス化と表示確認
class Test {
	public static void main(String args[]) {
		C objc = new C(); // CクラスをC型でインスタンス化
		System.out.println(objc.a); // 10
		B objb = new B(); // BクラスをB型でインスタンス化
		System.out.println(objb.a); // 20
		A obja = new B(); // BクラスをA型でインスタンス化
		System.out.println(obja.a); // 10
	}
}

プロトタイプチェーン

  • プロトタイプには、インスタンスを格納することができる
  • プロトタイプにインスタンスを格納することで、基となるオブジェクトのプロトタイプの中身を何代にも渡って継承できる
  • このプロトタイプの連鎖のことを、プロトタイプチェーンと呼ぶ
    • 現在のインスタンスにプロパティが存在するか検索
    • 見つからなければ、インスタンスの基になっているオブジェクトのプロトタイプを検索
    • さらにそのオブジェクトの基になるオブジェクトのプロトタイプを検索
    • 最終的には継承ツリー最上位のObject.prototypeを検索
    • それでも見つからなければundefinedを返す
  • 一度形成されたプロトタイプチェーンの関連付けはその時点の状態で保存され、その後変更しても影響しないので注意
// ConstructorAのプロトタイプに、aという変数を作成して10を代入
function ConstructorA () {}
ConstructorA.prototype.a = 10;
// ConstructorBのプロトタイプにConstructorAのインスタンスを格納
var ConstructorB = function() {}
ConstructorB.prototype = new ConstructorA();
// ConstructorBを基にobjを生成しているが、objからConstructorAのプロトタイプを参照できる
var obj = new ConstructorB();
console.log(obj.a); // 10

まとめ

  • コンストラクタのプロパティはインスタンスを生成するたびにメモリを確保するので、インスタンスごとに値が異なる変数の定義などで利用する
  • プロトタイプのプロパティは複数のインスタンスから共通のプロパティを参照されるため、メモリ領域を節約できる
  • プロトタイプで提供されたプロパティの値を変更したときには、別のメモリ領域にそのインスタンス自身のプロパティとして新たに生成される
  • コンストラクタのプロトタイプにプロパティを後から追加した場合でも、既に生成済みのインスタンスから新しいプロパティを参照できる
  • プロトタイプチェーンによってインスタンス間の継承関係を作れる

分からなかったところ

参考リンク thx!

boaoaboaoa 2012/12/27 05:23 var Box = function() {};
Box.prototype = { color: "red" };

var redBox = new Box();
alert(redBox.color); // red

Box.prototype = { color: "blue" };

var blueBox = new Box();
alert(redBox.color); // red
alert(blueBox.color); // blue


この時点で
redBox.prototype は { color: "red" } の参照を持っています
redBox.prototype と Box.prototype は { color: "blue" } です

そこでBox.prototypeが指すオブジェクトを改変しても
Box.prototype.color = "yellow";

redBox.prototypeが指すオブジェクトとは違うので影響を受けないということです
alert(redBox.color); // red
alert(blueBox.color); // yellow

tacamytacamy 2012/12/27 11:17 boaoaさん、コメントありがとうございます。
実はこの記事を書いた翌週にブログをはてなブログに移転してまして、そちらでさらに詳しく調べてみたりしました。
http://tacamy.hatenablog.com/entry/2012/12/17/000931

このおかげで、boaoaさんのおっしゃることも理解できるようになりました。
JavaScriptむずかしいけど楽しいですね〜!

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

トラックバック - http://d.hatena.ne.jp/tacamy/20121209/1355034499
このページの先頭へ