読者です 読者をやめる 読者になる 読者になる

Processing.jsが出力するJavaScriptを調べてみる(2)

職場で昼休みにモンストのマルチプレイが毎日開催されています。昼休みより後の時間にゲリラクエストを発生させるのはできれば止めてほしいです。

さて、今回は前回に引き続き、クラス継承時にProcessing.jsが出力するJavaScriptコードを調査してみたいと思います。(前回同様にマニアックな内容です)

クラス継承のJavaScriptコードの確認

クラスの継承を行った際に出力されるJavaScriptコードを調べるために、前回のクラス定義コードに以下を追加します。

class Student extends Person {
  Student(String name) {
    super(name);
  }

  void greet() {
    super.greet();
    println("And I'm also a student.");
  }
}

出力結果の差分は以下のとおりです。

var Student = (function () {
  function Student() {
    var $this_1 = this;
    var $super = {
      $upcast: $this_1
    };

    function $superCstr() {
      Person.apply($super, arguments);
      if (!('$self' in $super)) $p.extendClassChain($super)
    }

    function greet$0() {
      $super.greet();
      $p.println("And I'm also a student.");
    }
    $p.addMethod($this_1, 'greet', greet$0, false);

    function $constr_1(name) {
      $superCstr(name);
    }

    function $constr() {
      if (arguments.length === 1) {
        $constr_1.apply($this_1, arguments);
      } else $superCstr();
    }
    $constr.apply(null, arguments);
  }
  $p.extendStaticMembers(Student, Person);
  Student.$base = Person;
  return Student;
})();
$p.Student = Student;

$superCstr関数の処理内容が変わっているのと、最後にextendStaticMembersメソッドの呼び出しと$baseプロパティの設定が追加されています。

extendStaticMembers

静的なプロパティを基底クラスからコピーするメソッドです。引数にインスタンスではなくクラスを渡す点が異なるものの、実質的にはextendClassChainメソッドと同じ処理を行います。

インスタンス作成時の処理の流れ

クラスの定義、継承と処理内容を見てきましたが、最後にインスタンス作成時の処理内容を追ってみます。

Processing.jsは対象クラスのインスタンスが作成された際に、次のような流れで処理を行います。

  1. (クラス継承時のみ)$superという名前で基底クラスのインスタンスにするオブジェクトを作成
  2. addMethodメソッドで対象インスタンスに自クラスのメソッドを追加
  3. (クラス継承時のみ)$superに基底クラスのコンストラクタを適用し、基底クラスのプロパティを追加
  4. extendClassChainメソッド で、対象インスタンスに基底クラスのプロパティを追加
  5. 対象インスタンスに対して元コードのコンストラクタに記述された処理を実行

なかなか複雑な処理です。Processing.jsがオーバーロード等のJavaのクラスの仕組みを実現するために相当無理をしていることが分かります。

終わりに

Processing.jsをJavaScriptから直接使う際に、クラス定義の仕組みも利用できないか期待していたのですが、インスタンス作成時にかなり煩雑な処理を行っていることがわかりました。性能低下を避けるために、Processing.jsではクラスの定義や継承の多用は避けた方が無難そうです。