基底クラスのコンストラクタを呼び出す2005年12月05日 00時06分

【JavaScript】多重に派生されたクラスのコンストラクタで,基底クラスのコンストラクタを呼び出す方法」(Graviness Blog) にて基底クラスのコンストラクタを呼び出すという話題が出ていた。優乃さんは this から基底クラスのコンストラクタを呼び出したいようだが、JavaScript では this が何をさすかは文脈により異なってくる (「ECMAScript における this の意味」(Noncommutative Field) を参照)。なので個人的には基底クラスに関する操作はインスタンス (this) よりもクラス (コンストラクタ関数) から行ったほうがいいと思う。クラス名に依存したくないなら arguments.callee を用いればよい。

function Animal() {} // 基底クラス

function Dog() {
  this.__super__(); // this が何であるか (this.__super__ が何をさすか) は不定
}

function Cat() {
  Cat._superClass.apply(this); // クラス名に依存
}

function Swallow() {
  arguments.callee._superClass.apply(this); // クラス名に非依存、でもちょっと長い?
}

しかし実際に書き比べてみるとやはり this から呼び出せたほうが見通しがいいわけで。this から呼び出す際の問題は基底クラスのコンストラクタ内でも this.__super__ が変わらず、それ以上上のクラスのコンストラクタを呼び出せないこと。ならば基底クラスのコンストラクタを呼び出すときだけ __super__ を書き換えてやればいいのではないか。こんな感じで。

function inherit(subClass, superClass) {
  var Temp = new Function();
  Temp.prototype = superClass.prototype;
  subClass.prototype = new Temp;
  subClass.prototype.constructor = subClass;
  subClass.prototype.__super__ = function () {
    var originalSuper = this.__super__;
    this.__super__ = superClass.prototype.__super__ || null;

    superClass.apply(this, arguments);

    if (this.constructor == subClass)
      delete this.__super__;
    else
      this.__super__ = originalSuper;
  };
}

個人的に Class.prototype.constructor == Class というのは常に成り立っていてほしいのでそのように修正。基底クラスのコンストラクタ内で this.method() としても SuperClass#method ではなく SubClass#method が呼び出されるなどの不具合はあるが、コンストラクタ関数だけで完結する処理ならこれでうまく行くと思う。

function SuperSuperClass() {
  alert("SuperSuperClass Constructor");

  this.p = "supersuperclass";
}

function SuperClass() {
  alert("SuperClass Constructor");
  this.__super__(); // SuperSuperClass のコンストラクタを呼び出す.

  this.q = "superclass";
}

function SubClass() {
  alert("SubClass Constructor");
  this.__super__(); // SuperClass のコンストラクタを呼び出す.

  this.r = "subclass";
}

// 継承
inherit(SuperClass, SuperSuperClass);
inherit(SubClass, SuperClass);

var a = new SubClass();
alert(a.p); // "supersuperclass"
alert(a.q); // "superclass"
alert(a.r); // "subclass"