サイ本要約 9章 クラスとコンストラクタとプロトタイプ

JavaScript 第5版

JavaScript 第5版

9.1 コンストラクタ

記述例
function Rectangle(w, h){
    this.width = w;
    this.height = h;
    // return文は記述しない
}
var rect1 = new Rectangle(2, 4);
var rect2 = new Rectangle(8.5, 11);
オブジェクトの生成方法
  • オブジェクトリテラルで{}と記述する
    • ex.) var point = {x:0, y:0};
  • コンストラクタ関数を使う
    • ex.) var today = new Date();
コンストラクタ関数とは
  • オブジェクトのプロパティを初期化する関数
  • new演算子と一緒に使われる
  • オブジェクトの"クラス*1"を定義するにはコンストラクタ関数を定義するだけでよい
  • 通常、戻り値がない*2
thisとは
  • 実行中のコード*3が「自分自身」を表すオブジェクトにアクセスするためのキーワード
  • thisキーワードから参照されるオブジェクトは実行時にならないと決まらない
  • トップレベルのコード・関数内でthisを利用すると、『グローバルオブジェクト』を指す

参考:
"JavaScriptのthisキーワードをちゃんと理解する" http://builder.japan.zdnet.com/script/sp_javascript-kickstart-2007/20371112/1/
"JavaScript の this について" http://d.hatena.ne.jp/amachang/20070917/1190015123

new演算子の挙動
  1. 空のオブジェクトを生成する
  2. そのオブジェクトに対してプロトタイプ(後述)を設定する
  3. そのオブジェクトのメソッドとしてコンストラクタ関数を呼び出す

9.2 プロトタイプと継承

プロトタイプと継承とは
  • すべてのオブジェクトにはプロトタイプオブジェクトというオブジェクトへの参照が含まれている
  • オブジェクトはプロトタイプからプロパティを継承する*4
ややこしいこと
  • コンストラクタ関数のprototypeプロパティの値がオブジェクトのプロトタイプになる
  • すべての関数は定義時に自動的にprototypeプロパティが生成され、値が設定される
  • prototypeプロパティの初期値はconstructorプロパティしかプロパティがないオブジェクト
  • このconstructorプロパティはコンストラクタ関数自身を参照する*5

f:id:noire722:20110404201256p:image

つまり、すべてのオブジェクトで使うものは、prototypeオブジェクトのプロパティとして追加することで
プロパティの継承によってすべてのオブジェクトで利用可能になる。

9.3 JavaScriptの「クラス」

オブジェクトというデータ型をサポートするもののクラスという正式な概念はない。
しかし、前述のプロトタイプベースの継承を行うことが出来る。

用語の説明
  • インスタンスプロパティ
    • オブジェクトごとにそれぞれ異なる、データ値を持つプロパティ(9.1の記述例で言う width など)
  • インスタンスメソッド
    • インスタンスプロパティと似ているが、データ値ではなくメソッドである
    • インスタンスプロパティとの違いは自分専用のメソッドを持つとは限らないこと
  • クラスプロパティ
    • クラスに関連付けられたプロパティ

クラスプロパティの記述例

Rectangle.UNIT = new Rectangle(1, 1);

コンストラクタ関数(JavaScriptでは関数はオブジェクト)にプロパティUNITを追加することでクラスプロパティを実装している。

  • クラスメソッド
    • クラスに関連付けられたメソッド(Date.parse()など)
プライベートメンバ

JavaScriptではクロージャを使って実現する。

function ImmutableRectangle(w, h){
    // width と height の値がクロージャのスコープチェーン中に閉じ込められる
    this.getWidth = function(){ return w; }
    this.getHeight = function(){ return h; }
}

9.4 オブジェクト共通メソッド

JavaScriptで新しいクラスを定義する場合に、定義しておくべき関数

  • toString()メソッド
// 例
Circle.prototype.toString = function(){return "[Circle of radius " + this.r + "]"}
  • valueOf()メソッド
    • toString()と同じように使われるが、オブジェクトを文字列以外の基本型に変換する必要があるときに自動的に呼び出される
  • 比較用のメソッド
    • equals()メソッド
      • 「参照による比較」ではなく「値による比較」を行いたいときに使う
    • compareTo()メソッド
      • ソートする必要があるなら定義しておく

9.5 スーパークラスとサブクラス

JavaScriptではプロトタイプチェーンを使って実現する。

記述例
// スーパークラス
function Rectangle(w, h){
    this.width = w;
    this.height = h;
}
Rectangle.prototype.are = function(){return this.width * this.height;}

// サブクラス
function PositionedRectangle(x, y, w, h){
    // コンストラクタチェーン
    // スーパークラスのコンストラクタを呼び、プロパティを初期化
    Rectangle.call(this, w, h);
    
    // 長方形の左上の座標を保存
    this.x = x;
    this.y = y;
}

// Rectangle のサブクラスにするにはプロトタイプオブジェクトを明示的に生成する
// デフォルトのプロトタイプオブジェクトではObjectの直接のサブクラスになるため
PositionedRectangle.prototype = new Rectangle();

// スーパークラスのwidth, heightプロパティは継承したくないので削除
delete PositionedRectangle.prototype.width;
delete PositionedRectangle.prototype.heith;

// プロトタイプオブジェクトはRectangle()コンストラクタと一緒に生成されるので
// Rectangle()を参照するconstructorプロパティを持つ。
// PositionedRectangle()コンストラクタを参照するように書き換える
PositionedRectangle.prototype.constructor = PositionedRectangle;

// 準備完了
// インスタンスメソッドを追加してみる
PositionedRectangle.prototype.contains = function(x, y){
    return (x > this.x < this.x + this.width && y > this.y && y < this.y + this.height);
}
var r = new PositionedRectangle(2, 2, 2, 2);
print(r.contains(3, 3)); // インスタンスメソッド
print(r.area()); // 継承したインスタンスメソッド
コンストラクタチェーン

サブクラスのコンストラクタ関数内で明示的にスーパークラスのコンストラクタ関数を呼び出す処理のこと。

// 1階層のサブクラスしか持たない場合はサブクラスのプロトタイプオブジェクトに
// superclassという名前のプロパティを追加すれば簡潔に書ける
PositionedRectangle.prototype.superclass = Rectangle;
function PositionedRectangle(x, y, w, h){
    this.superclass(w, h);
    this.x = x;
    this.y = y;
}
オーバーライド

サブクラスでスーパークラスと同じ名前のメソッドを定義すると、サブクラスはそのメソッドをオーバーライドする。

*1:JavaScript2.0やその他の言語の"クラス"とは別物である

*2:しかし、コンストラクタから別のオブジェクトを返すことは可能。そうすると返されたオブジェクトがnew式の値になり、thisの値であったオブジェクトは破棄される。

*3:関数(メソッド)などの実行可能なコード。つまり、thisキーワードはメソッドをメンバに含むオブジェクトを指すと言える。

*4:プロトタイプオブジェクトのプロパティは、このプロトタイプオブジェクトを参照するオブジェクトのプロパティとして使える。

*5:すべてのオブジェクトがconstructorプロパティを持つ。