サイトトップ

Director Flash 書籍 業務内容 プロフィール

HTML5テクニカルノート

CreateJS 14/12/12: 新たな継承の仕組みを定めるextend()とpromote()メソッド

ID: FN1411002 Technique: HTML5 and JavaScript Library: CreateJS 14/12/12

ユーティリティメソッド
モジュール CreateJS
extend()メソッド
文法 extend(subclass, superclass)
概要

新たなサブクラスに、継承するスーパークラスへのプロトタイプチェーンを与えるとともに、プロトタイプオブジェクト(Function.prototypeプロパティ)のObject.constructorプロパティを定める。このメソッドは、サブクラスのコンストラクタを定めたすぐ後に、呼出さなければならない。

引数

subclass − 新たに継承を与えるサブクラスのコンストラクタ関数。

superclass − サブクラスに継承させたいスーパークラスのコンストラクタ関数。

戻り値

継承が与えられたサブクラスのFunction.prototypeプロパティ

promote()メソッド
文法 promote(subclass, prefix)
概要

オーバーライドされたスーパークラスのメソッドの参照を、サブクラスのプロトタイプオブジェクトに加える。スーパークラスのメソッドおよびコンストラクタは、サブクラスのインスタンスから直接つぎの名前で参照する。

  • メソッド: prefix_メソッド名
  • コンストラクタ: prefix_constructor

接頭辞(prefix)にはスーパークラス名を使うことが望ましい。このメソッドは、サブクラスのプロトタイプをすべて定めてから、呼出さなければならない。

引数

subclass − オーバーライドしたスーバークラスのメソッドを参照したいサブクラスのコンストラクタ。

prefix − 接頭辞としてメソッドに加える文字列。スーパークラスの名前とするのが望ましい。

戻り値

オーバーライドしたスーパークラスのメソッドの参照が備わったサブクラスのコンストラクタ関数。


説明

CreateJSモジュールのユーティリティメソッドは、それぞれが別のJavaScriptファイルになっています。名前空間createjsに続けてメソッドを直接呼出します。

スーパークラスを継承するとき、JavaScriptコードでは多くの場合、コンストラクタのFunction.prototypeプロパティをスーパークラスのオブジェクトで書替えます(「クラスの継承と透視投影」の「Shapeクラスを継承する3次元座標のクラス定義」参照)。これまでのCreateJSでも、このかたちで継承が行われていました。

function サブクラス() {
  // インスタンスの初期化
}
サブクラス.prototype = スーパークラスのオブジェクト;

そのやり方に変わって新たに備わったのが、extend()メソッドです。サブクラスとスーパークラスのコンストラクタをふたつの引数としてメソッドに渡せば、継承がかたちづくられます。CreateJSのクラスの継承は、これから内部的にはextend()メソッドで行われることになりました。

function サブクラス() {
  // インスタンスの初期化
}
createjs.extend(サブクラス, スーパークラス);

なお、extend()メソッドは、サブクラスのコンストラクタ関数のプロトタイプオブジェクトを上書きします。したがって、コンストラクタを定めたすぐ後に呼出さなければなりません。

promote()メソッドを用いると、サブクラスからオーバーライドしたスーパークラスのメソッドが呼出せます。メソッド第2引数の接頭辞にスーパークラス名を与えれば、サブクラスに対して「スーパークラス_メソッド」で参照が得られるようになります。このとき、スーパークラスのコンストラクタは「スーパークラス_constructor」で参照されます。

createjs.promote(サブクラス, "スーパークラス名");

function.call()Function.apply()メソッドでスーパークラスのメソッドをサブクラスのオブジェクトに対して呼出すより、高いパフォーマンスが得られます。なお、スーパークラスのメソッドがオーバーライドされているかどうか確かめますので、promote()メソッドはサブクラスのFunction.prototypeプロパティにメソッドをすべて定めてから呼出さなければなりません。


以下のコード001は、extend()メソッドでサブクラス(MySubClass)にスーパークラス(MySuperClass)を継承します(第5行目)。サブクラスのオブジェクト(foo)はinstanceof演算子でスーパークラスを継承することが確かめられ、constructorプロパティによりサブクラスのコンストラクタが参照されます(第7〜8行目)。スーパークラスのコンストラクタは呼出されませんので、その本体のステートメント(第2行目)は実行されません。

コード001■extend()メソッドでクラスを継承する
  1. function MySuperClass() {
  2.   console.log("the super class is called");
  3. }
  4. function MySubClass() {}
    // MySubClass.prototype = new MySuperClass();
  5. createjs.extend(MySubClass, MySuperClass);
  6. var foo = new MySubClass();
  7. console.log(foo instanceof MySuperClass);   // true
  8. console.log(foo.constructor === MySubClass);   // true

extend()メソッドを使わず(第5行目)、サブクラス(MySubClass)のFunction.prototypeプロパティにスーパークラス(MySuperClass)のオブジェクトを定めても継承はできます(第5行目上のコメント行)。けれど、この場合constructorプロパティは、スーパークラスのコンストラクタを参照します(第8行目の結果はfalse)。また、スーパークラスのコンストラクタを呼出しますので、その本体の処理が実行されます(第2行目)。

以下のコード002は、2次元座標と3次元座標のふたつのクラス(Vector2DとVector3D)を定めます。前者はCreateJSのPointクラス、後者は2次元座標のクラスを、extend()メソッドで継承しました(第4および第14行目)。ふたつのクラスはともに座標をコンストラクタの引数として受取り(第1および第10行目)、原点からその座標までの距離を同じ名前のメソッド(getLength())で返します(第5〜8行目および第15〜19行目)。

そこで、どちらのクラスもpromote()メソッドにスーパークラス名を接頭辞の引数に渡して呼出しました(第9および第20行目)。すると、それぞれのコンストラクタからスーパークラスのコンストラクタは、「this.スーパークラス_constructor()」で呼出せます(第2行目および第11行目)。また、オーバーライドしたスーパークラスのメソッド(getLength())も、クラス名の接頭辞つきで参照しています(第16行目)。

コード002■3次元座標のクラスから2次元座標のオーバーライドした距離のメソッドを呼出す
  1. function Vector2D(x, y) {
  2.   this.Point_constructor(x, y);
  3. }
  4. createjs.extend(Vector2D, createjs.Point);
  5. Vector2D.prototype.getLength = function() {
  6.   var square = this.x * this.x + this.y * this.y;
  7.   return Math.sqrt(square);
  8. };
  9. createjs.promote(Vector2D, "Point");

  10. function Vector3D(x, y, z) {
  11.   this.Vector2D_constructor(x, y);
  12.   this.z = z;
  13. }
  14. createjs.extend(Vector3D, Vector2D);
  15. Vector3D.prototype.getLength = function() {
  16.   var length2D = this.Vector2D_getLength();
  17.   square = length2D * length2D + this.z * this.z;
  18.   return Math.sqrt(square);
  19. };
  20. createjs.promote(Vector3D, "Vector2D");

  21. var vector = new Vector3D(1, 1, 1);
  22. console.log(vector.getLength());   // 1.7320508075688774

なお、3次元空間の原点からxyz座標までの距離は、xy平面におけるxy座標とz座標を三平方の定理で2度組合わせて求めることができます(図001)。原点(0, 0, 0)から(1, 1, 1)までの距離は√3です(第21〜22行目)。

図001■原点からxy座標までの距離にz座標を三平方の定理で組合わせる


実装

extend()メソッドの実装は、つぎのとおりです。メソッド内でクラス(o)を定め、プロトタイプオブジェクトはスーパークラスから参照を得て、Object.constructorプロパティにはサブクラスのコンストラクタを与えています(第3〜4行目)。そして、そのインスタンスをサブクラスのプロトタイプオブジェクトに定めて返します(第5行目)。

  1. createjs.extend = function(subclass, superclass) {
  2.   "use strict";
  3.   function o() { this.constructor = subclass; }
  4.   o.prototype = superclass.prototype;
  5.   return (subclass.prototype = new o());
  6. };

extend()promote()メソッドにより、CreateJS内部のクラスの基本的な実装も変わりました。

たとえば、これまでのContainerクラスは、DisplayObjectを以下のようなやり方で継承していました。まず、サブクラスのコンストラクタのFunction.prototypeプロパティをスーパークラスのオブジェクトで置替えます(第4行目)。つぎに、コンストラクタはinitialize()メソッドを呼出すだけです(第1〜3行目)。その代わり、スーパークラスのinitialize()メソッドは別の名前に置換えてから、自らのinitialize()メソッドを定めなければなりません(第5〜8行目)。

  1. function Container() {
  2.   this.initialize();
  3. }
  4. Container.prototype = new createjs.DisplayObject();
  5. Container.prototype.DisplayObject_initialize = Container.prototype.initialize;
  6. Container.prototype.initialize = function() {
  7.   this.DisplayObject_initialize();
      // ...[中略]...
  8. };

EaselJS 0.8.0では、Containerクラスの実装はつぎのようにextend()メソッドで継承されます(第4行目)。また、promote()メソッドによりスーパークラスのコンストラクタがたやすく参照できるので、initialize()メソッドはもはや定めません(第5行目および第2行目)。

  1. function Container() {
  2.   this.DisplayObject_constructor();
      // ...[中略]...
  3. }
  4. createjs.extend(Container, createjs.DisplayObject);
    // ...[中略]...
  5. createjs.Container = createjs.promote(Container, "DisplayObject");


作成者: 野中文雄
更新日: 2015年6月5日 リンクと若干の文言の修正。
更新日: 2014年12月20日 2014年12月12日付の正規リリースにともない、タイトルおよび細かな内容を書直すとともに、公式サイトのドキュメントへのリンクを加えた。
作成日: 2014年11月18日


Copyright © 2001-2014 Fumio Nonaka.  All rights reserved.