Y's note

Web技術・プロダクトマネジメント・そして経営について

本ブログの更新を停止しており、今後は下記Noteに記載していきます。
https://note.com/yutakikuchi/

javascriptのクラスまとめ

クラスの概念

  • Javascriptにも一応クラスみたいなものといった概念が存在しますが、多言語のクラスに比べると規制が緩いもののようです。
  • javascriptではprototypeといったものをベースとしたオブジェクト指向として考えられています。(これはあとで説明)
  • prototypeのチェーンといった方法を利用して継承を実装することも可能です。

シンプルなインスタンス

関数で定義されたクラス的なものをnewすることでインスタンスを生成することが出来ます。下にサンプルコードを記述します。

var Parent = function() { //ある意味コンストラクタみたいなもの
   this.name = 'Parent'; //クラスのプロパティはthisで表現する。
   this.echo = function() { 
      alert( this.name );
   }
}

var instance = new Parent(); //インスタンス化
instance.echo();  // Parent

Prototypeの利用

ここがprototypeベースのオブジェクト指向の内容になります。上のインスタンス化の方法にはクラス的なものをnewするときにプロパティやメソッドをまるまるメモリに展開してしまうため、newの数が増えるたびに無駄な領域確保が発生してしまいます。そこでPrototypeといった属性を利用します。Prototypeを利用することにインスタンスからの参照を暗黙的参照に切り替えることが出来ます。コードを例に見てみます。

var Parent = function() {
      this.name = 'Parent';
}

// prototypeを利用
Parent.prototype.echo = functio() {
   alert( this.name ); // Parent
};

var instance = new Parent();
instance.echo(); //暗黙的な参照が行われる。

newしたときにParentが展開されます。instance.echo()のコールではまずParentがechoというメソッドを所有するかどうかを観に行きます。実際にはParent自体は所有していません。ところがParentが所有していないとjavascriptが解釈するとprototype属性以下のメソッドを探索の対象にします。prototype以下にはecho()メソッドが存在するのでそこを利用します。インスタンス自体がメソッドを所有しているかどうかのチェックは次のコードで確認できます。インスタンスはechoというメソッドを直接所有していません。prototypeは直接所有しています。

alert( instance.hasOwnProperty( 'echo' ) ); //false
alert( Parent.prototype.hasOwnProperty( 'echo' ) );  //true

Prototypeを利用した継承

Prototypeを利用すると継承を実現できます。実際にはPrototypeのチェーンというものにより継承が成り立ちます。以下はサンプルコードになります。

//親クラス
var Parent = function(){} 

//親のprototype
Parent.prototype = { 
    name : 'parent',
    method : function() { 
        alert( this.name ); 
    }
};

//子クラス
var Child = function(){} 

//継承
Child.prototype = new Parent(); 

//property追加
Child.prototype.name = 'child';

//無名関数とapplyを使った定義
(function() { 
    this.test = 'test';
    this.echo = function() {
        alert( this.test );
    }
}).apply( Child.prototype )

//子クラスの呼び出し
var childClass = new Child();
childClass.echo(); //test
childClass.method(); //child

Child.prototype = new Parent(); このように記述するとParentを継承することが出来ます。子どもの方ではprototype.property名 = value;といったようにprototypeを羅列するものOKですが、無名関数とapplyをうまく使うとすっきりしたコードを書くことが可能です。(サンプルでは両方で記述しています。)実は継承の書き方は複数ありますが、私は上のprototypeパターンしか知りませんでした。以下のページにその他の方法も沢山書いてあります。素晴らしくよくまとまっています。
http://d.hatena.ne.jp/shogo4405/20070121/1169394889

new演算子について

new演算子の内部では何をやっているのだろうというのを検証してみました。newした後に以下のコードを実行してみるとtrueとなります。

alert( childClass.__proto__ == Child.prototype ); //true

どうやらinstanceの__proto__属性に対してChild.prototypeを格納しているようです。他にも引数の受け取りやinstanceのreturnも行っていると思うのですが、正確な挙動が分からなかったので検索してみました。検索した結果とてもよいページを見つけました。newの内部的なコードは次のようになるようです。

Function.prototype.construct = function () {
  var newInstance = new Object();
  if (this.prototype instanceof Object)
    newInstance.__proto__ = this.prototype;

  var returnValue = this.apply(newInstance, arguments);
  if (returnValue instanceof Object)
    return returnValue;
  return newInstance;
};
var o = F.construct(arg); // == new F(arg)

流れは
・オブジェクト生成。
・__proto__に対してprototypeを設定。
インスタンスに対して引数をapplyする。
インスタンス/オブジェクトを返却。
※上のコードは以下のページより引用させていただきました。
http://nanto.asablo.jp/blog/2005/10/24/118564