JavaScriptのClassについて part5

# 初めに
今回はクラスの基本概念`instanceof`演算子と、単一継承でのミックスインの実現をまとめていきたいと思います。

参考文章はこちらです。
[Class checking: "instanceof"](https://javascript.info/instanceof)
[Mixins](https://javascript.info/mixins)

# instanceof
インスタンスと親の所属関係を表す演算子です。プロトタイプチェーンのチェックにもよく使われています。
基本の構文：[instanceof - MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/instanceof#%E6%A7%8B%E6%96%87)
```jsx
object instanceof constructor
```

インスタンスの関連図は前の文章でもよく使っていました。
[JavaScriptの.__proto__とprototypeとinstance](https://qiita.com/hu-yu/items/e1b7d59d9f406e73977b)
[JavaScriptのClassについて part4](https://qiita.com/hu-yu/items/1d6a418fbbdbf76b7748)

オブジェクトが複数の親を持つインスタンスであることが可能です。
例えば、クラスの場合は`extends`で拡張したら自分のインスタンスは拡張先（インスタンスにとってもうひとつの親）の所属関係を結びつけることができ、そしてpart3 [this in static methods](https://qiita.com/hu-yu/items/e4529a08e902dcd12a6e#this-in-static-methods)の検証でわかったように、拡張先の静的メソッドやプロパティ以外は利用できるようになります。

ただプロトタイプチェーンの`.__proto__`も、`extends`も一つの親に限られているので勝手に変えたらパフォーマンスが落ちたり全く違う結果に導いたりします。インスタンスならこういう問題ありません、複数の親を持っても問題にならないです。

でも、プロトタイプチェーンを変えず`extends`拡張も使わず、果たしてインスタンスの所属関係だけで親へのアクセスが実現できるのかな？

## [Symbol.hasInstance]
参考文章の例を借りて検証を入れてみました。
```jsx
class Animal {
  static [Symbol.hasInstance](obj) {
    if (obj.canEat) return true
  }

  constructor() {
    this.name = 'Taro'
    this.constructorMethod = () => {
      console.log('Hi')
    }
  }

  sayHi() {
    return `Hi, ${this.name}`
  }
}

let obj = {
  canEat: 1,
  name: 'Mick'
}

console.log(obj instanceof Animal) // true
console.log(obj instanceof Object) // true
console.log(obj instanceof Function) // false
console.log(obj.name) // Mick
console.log(obj.sayHi()) // TypeError: obj.sayHi is not a function
console.log(obj.constructorMethod()) // TypeError: obj.constructorMethod is not a function
console.log(obj.sayHi()) // TypeError: obj.sayHi is not a function
```
確かに`Symbol.hasInstance`の作用で`obj`が`class Animal`のインスタンスになったけれど、実際`instanceof`の結果を変えても
```jsx
console.log(Object.getPrototypeOf(obj) === Object.prototype) // true
console.log(Object.getPrototypeOf(obj) === Animal.prototype) // false
console.log(Object.getPrototypeOf(obj) === Animal) // false
```
`obj`のプロトタイプチェーン何も変わらなかったです。`instanceof`演算子の結果だけを変えてもプロトタイプチェーンは変えられません。
このメソッドは初見でどこに使われるかはよくわかりませんが、自分の感覚では単にインスタンスの管理なら使えると思います。
```jsx
class Animal {
  static [Symbol.hasInstance](obj) {
    if (obj.myMethod === this.prototype.myMethod) return true
  }

  myMethod() {
    console.log('Animal Method')
  }
}

function myMethod() {
  console.log('Global Method')
}

let obj1 = {
  myMethod: Animal.prototype.myMethod
}

let obj2 = {
  myMethod: myMethod
}

obj1.myMethod() // Animal Method
obj2.myMethod() // Global Method

console.log(obj1 instanceof Animal) // true
console.log(obj2 instanceof Animal) // false
```

## Object.prototype.toString()
基本の使い方
```jsx
console.log(Object.prototype.toString(obj1)) // [object Object]
console.log(obj1.toString()) // [object Object]

console.log(Object.prototype.toString.call([])) // [object Array]
console.log(Object.prototype.toString.call(null)) // [object Null]
console.log(Object.prototype.toString.call(123)) // [object Number]
console.log(Object.prototype.toString.call(new Date())) // [object Date]
```
ここから少し別の話ですが、
[Object.prototype.toString()](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/toString)、MDNの説明では`toString`を上書きできると書いてあります。
```jsx
console.log(Animal.prototype) // {}
console.log(Animal.prototype.toString) // [Function: toString]
console.log(Animal.prototype.hasOwnProperty('toString')) // false
// Animal.prototype => Object.prototype.toString

Animal.prototype.toString = function () {
  return `${this.name}`
}

console.log(Animal.prototype) // { toString: [Function (anonymous)] }
console.log(Animal.prototype.toString) // [Function (anonymous)]
console.log(Animal.prototype.hasOwnProperty('toString')) // true
// Animal.prototype.toString

console.log(cat.toString()) // cat
```
でも`Animal.prototype`で新設された`toString`が先に見つかったことで返されたのではないかと。

## [Symbol.toStringTag]
このメソッドでクラスの独自のオブジェクトタグをつけられます。
```jsx
let user1 = {
  id: 1,
  [Symbol.toStringTag]: 'User'
  // this property has no length
}

let user2 = {
  id: 2,
  [Symbol.toStringTag]: 'Admin'
}
console.log(Object.prototype.toString.call(user1))
// [object User]
console.log(user1[Symbol.toStringTag])
// User
```
そして↓も[Symbol.toStringTag](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toStringTag) MDNの例を参考して書いてみました。
```jsx
class ValidatorClass {
  constructor(obj) {
    // console.log(Object.keys(obj).length) // 1
    if (Object.keys(obj).length > 0) {
      this.obj = obj
    }
  }

  get [Symbol.toStringTag]() {
    if (this.obj[Symbol.toStringTag] === 'User') {
      return 'ValidUser'
    } else if (this.obj[Symbol.toStringTag] === 'Admin') {
      return 'ValidAdmin'
    }
  }
}

console.log({}.toString.call(new ValidatorClass(user1)))
// [object ValidUser]
console.log({}.toString.call(new ValidatorClass(user2)))
// [object ValidAdmin]
```
最初は空のオブジェクトなら排除してほしいから`if (Object.keys(obj).length > 0)`を設定してたけど、`[Symbol.toStringTag]`はlengthがないの知らなかったのでいろんな試行錯誤で苦戦してやっとわかりました。（`user1`、`user2`にそのあとidを付けました。）
MDNもう一度みたら、このプロパティは列挙不可と書いてあったの...。

# Mixins
## Object.assign()
Object.assign()はディープコピーを実現できるメソッドです。（元のオブジェクトに同じプロパティ名がすでに存在してたら上書きします。）
↓は基本の例です。
```jsx
let mixinMethods = {
  sayHi() {
    console.log(`Hello, ${this.name}`)
  },
  sayBye() {
    console.log(`Bye, ${this.name}`)
  }
}

class User {
  constructor(name) {
    this.name = name
  }
}

Object.assign(User.prototype, mixinMethods)
new User('Mick').sayHi() // Hello, Mick
```
コピーしたメソッドを自分の`.prototype`に置くので、`extends`しても問題ありません。自分のインスタンスのプロトタイプチェーンが変えられなければもちろんメソッドたちが使えます。

↓は`Object.assign`を応用した別の方法です。
```jsx
let sayMethod = {
  say(phrase) {
    console.log(phrase)
  }
}

let sayMixin = {
  __proto__: Object.create(sayMethod), // return new object
  sayHi() {
    super.say(`Hello, ${this.name}`)
  },
  sayBye() {
    super.say(`Bye, ${this.name}`)
  }
}

class User {
  constructor(name) {
    this.name = name
  }
}

Object.assign(User.prototype, sayMixin)
new User('Lucy').sayBye() // Bye, Lucy
```
プロパティ`__proto__`を操作することで、`sayHi()`と`sayBye()`のなかで`super`経由して`__proto__`である`sayMethod`オブジェクトにアクセスし`say()`を利用した。
そして`sayMixin`がコピーされ`User.prototype`に置かれても`super`の特性でメソッドの`this`を自分の内部プロパティ[[HomeObject]]へ参照させるため、`say()`を利用した`sayMixin`の`sayHi()`と`sayBye()`が自分の`__proto__`へたどり着いた。
`super()`でオブジェクトの`this`を[[HomeObject]]へ参照させるのが前の文章では書いてありますすが、メソッドも[[HomeObject]]プロパティが持ってるのここで初めて知りました。

## EventMixin
完全に初見なので[参考文章の例](https://ja.javascript.info/mixins#ref-2818)をそのまま使用しました。
```jsx
let eventMixin = {
  on(eventName, handler) {
    // if event Handler does not exist, create it
    if (!this._eventHandlers) this._eventHandlers = {}
    // if there didn't exist a matched event name, according to the event name(property name), assign []
    if (!this._eventHandlers[eventName]) {
      this._eventHandlers[eventName] = []
    }
    // find the matched array and push handler(callback function)
    this._eventHandlers[eventName].push(handler)
  },
  off(eventName, handler) {
    // select eventHandler and its matched eventHandler name property
    let handlers = this._eventHandlers && this._eventHandlers[eventName]
    if (!handlers) return
    for (let i = 0; i < handlers.length; i++) {
      // note: why use == ?
      if (handlers[i] == handler) {
        // delete the matched handler property and start at the same index (i--)
        handlers.splice(i--, 1)
      }
    }
  },
  trigger(eventName, ...args) {
    // if didn't exist event handlers or event name then return
    if (!this._eventHandlers || !this._eventHandlers[eventName]) return
    // if it exist, call all of the event handlers, and put arguments to create those events
    this._eventHandlers[eventName].forEach(handler => handler.apply(this, args))
  },
}
```
注釈の英語は説明に合わせて自分なりの解釈です。正しいかどうかわかりませんが。
`on()`はセットメソッド、`off()`は削除メソッド、`trigger()`はイベントのトリガーです。
```jsx
class Menu {
  choose(value) {
    // call trigger
    this.trigger('select', value)
  }
}

Object.assign(Menu.prototype, eventMixin)

let menu = new Menu()
// set 'select' event
menu.on('select', value => console.log(`Value selected: ${value}`))
menu.choose('123') // Value selected: 123
```
例の使い方としては、さきに`class`に欲しいイベントトリガー（イベント名とコールバック関数）を設置しておいて、`on()`でイベント名に応じてメソッドの詳しい動作を設定し、最後は`class`のメソッド`choose()`を呼び出し、値を入れて処理させてもらいました。

イベントミックスインの応用は完全に理解するまで自分にはまだまだ難しいと思いますが、今のところ`class`への勉強はとりあえずここまでです。