音ゲー、fps、DTM、プログラミング雑記置き場

ブログタイトル通りに雑記を垂れ流す。

【JavaScript】まってくださいプロトタイプベースオブジェクト指向とわ!? #1

  • C#Javaとかで書いてたクラスからインスタンス生成という概念がJavaScriptにない
    • 上記はクラスベースオブジェクト指向でJSとはそもそも立ち位置が違う
    • JavaScriptはプロトタイプベースオブジェクトらしい
      • なのでクラスベースの言語とはコンストラクタとか継承の仕方は異なる
      • ES6でJavaScriptclass構文が用意されたけど、
        これはクラスベースチックに書くための糖衣構文。内部はプロトタイプベースで動いている
  • 便宜上JSでもクラスと言っているが後述するようにクラスも一オブジェクトにすぎない

2つのオブジェクト指向の記法について

  • プロトタイプベースだと、すべてをオブジェクトと考えインスタンス生成も
    オブジェクトから別のオブジェクトを生成しているだけと捉えている

    • クラスベースだとクラスからNewでインスタンスが作成され初めて
      実体(オブジェクト)を持つみたいな言い方をよくする
    • ようはプロトタイプは登場人物はすべてオブジェクト、
      クラスタイプはクラスからオブジェクトが作成される(クラスが上位存在)
  • 幸い、JSはclass構文でクラスベースチックに書くこともできるので、
    両者を書いて差分を見てみる

  • ソースはMDN Web Docsより一部改変している
  • JavaScript のクラス(MDN Web Docs)
/* prototypeプロパティを用いたプロトタイプベースな記法 */
function Person(name){
  this.name = name;
}
Person.prototype.greeting = (name) => {
  console.log(`I'm ${name}`);
}
//子クラスの宣言
function Human(name,tribe){
  this.name = name;
  this.tribe = tribe;
}

//Human(子)クラスのprototypeオブジェクトに親クラスの参照をセットすることで継承を実装
Human.prototype = new Person();
//Human(子)クラスでの追加メソッドは親クラスのインスタンス宣言時より後で行わないと、
//prototypeプロパティがクラス間で共有されるため親のprototypeで上書きされ、無効化される
Human.prototype.greeting2 = (name,tribe) => {
  console.log(`I'm ${name} and ${tribe}`);
}

//インスタンス(オブジェクト)生成
const man = new Human("Buccellati","Human");
man.greeting(man.name); //I'm Buccellati
man.greeting(man.tribe); //I'm Human
man.greeting2(man.name,man.tribe); //I'm Buccellati and Human
  • JSではすべてのオブジェクトにprototypeプロパティが自動付与されることで
    プロトタイプベースを実現している
  • クラスの継承も親子オブジェクト間でprototypeを共有することで可能
  • また、コンストラクタは関数オブジェクト全体を指す
/* class構文用いたクラスタイプチックな記載 */
class Person{
  constructor(name){
    this.name = name;
  }
  greeting = () => {
    console.log(`I'm ${this.name}`);
  }
}

//子クラスの宣言
class Human extends Person{
  constructor(name,tribe){
    //superメソッドで親クラスのコンストラクタを実行(nameプロパティの初期化を行う)
    super(name);
    this.tribe = tribe;
  }
  greeting2 = () => {
    console.log(`I'm ${this.name} and ${this.tribe}`);
  }
}

const man = new Human("Joruno","Human");
console.log(man.name); //I'm Joruno
console.log(man.tribe); //I'm Human
man.greeting2(); //I'm Joruno and Human
  • 内部含めて動作はprototypeプロパティ使用したソースと同様

prototypeプロパティとプロトタイプチェーン

  • JSのNew演算子が実行されると右辺のオブジェクトを参照し、
    コンストラクタ実行後、左辺へ生成したオブジェクトを格納する
    • クラスタイプだとNew演算子で右辺のクラス情報を元に左辺へ
      インスタンス(オブジェクト)が作成される
    • だいたいの動作は同じ。先述もしたがクラスもオブジェクトとみなすのが
      プロトタイプベース
  • コンストラクタが関数オブジェクトのため、prototypeプロパティも持つ
    • 参照先に存在しないメンバはこのプロパティ経由でさらにメンバを検索する
    • 上記ソース例だとgreetingメソッドはHumanクラス→Human.prototypeの検索で   親クラス参照(Personクラス)を見つけさらにPersonクラス→Person.prototype
      対象メソッドを見つける
      • このprototypeプロパティによる複数オブジェクト間の
        探索をプロトタイプチェーンと呼ぶ

まとめ

  • JavaScriptJavaみたいなクラス→インスタンスの関係はないよ
  • JavaScriptはプロトタイプベースOOPのため上記の関係は
    オブジェクト→別オブジェクトに過ぎないよ
  • どのオブジェクトもprototypeプロパティをもっていてこいつを経由で
    大抵のメンバを探索できるよ(プロトタイプチェーン)
  • TODO:#2でプロトタイプとクラスそれぞれのOOP
    有用性を書けるようになったら書く