オブジェクト指向構文について

* オブジェクト指向構文(5章関連) inherit.htmlを例に、いろいろ試してみよう テキストのサンプル inherit.html
<script type="text/javascript">
    function initializeBase(derive, base, baseArgs) {
	base.apply(derive, baseArgs);
	for(prop in base.prototype) {
	    var proto = derive.constructor.prototype;
	    if(!proto[prop]) {
		proto[prop] = base.prototype[prop];
	    }
	}
    }

  // Memberクラスの定義
  var Member = function(firstName, lastName) {
      this.firstName = firstName;
      this.lastName = lastName;
  };

  Member.prototype.getName = function() {
      return this.lastName + " " + this.firstName;
  };

  var SpecialMember = function(firstName, lastName, role) {
      initializeBase(this, Member, [firstName, lastName]);
      this.role = role;
  };

  SpecialMember.prototype.isAdministrator = function(){
      return (this.role == 'Administrator');
  }

  var mem = new SpecialMember('祥浩', '山田', 'Administrator');
  document.writeln('名前:' + mem.getName() + '<br/>');
  document.writeln('管理者:' + mem.isAdministrator()+'<br/>');

「クラス」でのメソッド定義

クラスを「」にしているのは、「クラスもどき」であり、クラス定義とオブジェクト(インスタンス)生成というJavaやC++の概念と少し違うためである。

Memberクラスの一メソッドとしてgetName()を定義する方法は、inherit.htmlの例の他に、直接Memberの定義として

  var Member = function(firstName, lastName) {
      this.firstName = firstName;
      this.lastName = lastName;
      this.getName = function() {
          return this.lastName + " " + this.firstName;
      };
  };

のようにもできる(5.1節)。しかし、メモリーを無駄に使うので、推奨しないテキストには書いてあることに注意(5.2節)

クラスのprototypeを引き継ぐ

クラス定義の変数(プロパティ)に値を与えて引き継ぐには、initializedBaseの1行目

   base.apply(derive, baseArgs);
一般的には、
  メソッド名.apply(オブジェクト名, メソッドに与える引数)
でOK.

プロパティ(メソッド)群を引き継ぐために

for(prop in base.prototype) {
    var proto = derive.constructor.prototype;
    if(!proto[prop]) {
        proto[prop] = base.prototype[prop];
    }
}
を行なっている。引き継ぐ側(derive、具体的にはmem)のprototypeとしてMemberのprototypeをひとつひとつコピーするのである。(constructorというのを付けなければならないのが、気づきにくい。)

試しに、次を実行してみる。

<meta charset="utf-8">
<script type="text/javascript">
    function initializeBase(derive, base, baseArgs) {
        base.apply(derive, baseArgs);
        /*
        for(prop in base.prototype) {
            var proto = derive.constructor.prototype;
            if(!proto[prop]) {
                proto[prop] = base.prototype[prop];
            }
        }
        */
    }

  // Memberクラスの定義
  var Member = function(firstName, lastName) {
      this.firstName = firstName;
      this.lastName = lastName;

      /* prototypeを用いないメソッドの定義(オブジェクトで使用できる)
         メモリーを無駄に使うので、推奨しないテキストには書いてある
       */
      this.getName = function() {
          return this.lastName + " " + this.firstName;
      };
  };

  /*
  Member.prototype.getName = function() {
      return this.lastName + " " + this.firstName;
  };
  */

  var SpecialMember = function(firstName, lastName, role) {
      initializeBase(this, Member, [firstName, lastName]);
      this.role = role;
  };

  SpecialMember.prototype.isAdministrator = function(){
      return (this.role == 'Administrator');
  }

  var mem = new SpecialMember('祥浩', '山田', 'Administrator');
  document.writeln('名前:' + mem.呼dgetName() + '<br/>');
  document.writeln('管理者:' + mem.isAdministrator()+'<br/>');
  document.writeln('姓: ' + mem.lastName); // 直接プロパティを参照する
</script>

Memberの定義に直接getName()を書きこんであるので、問題なく実行できる。

しかし、prototypeで定義したテキストの例では、mem.getName()は未定義だよというエラーになる。

constructorの中身

initializedBase()の中に

 document.writeln(derive.constructor);
のように、constructorに何が入っているかを出力してみよう。 (文字列として、deriveの定義自体が入っていることがわかる。)