JavaScriptのClassについて part2

# 初めに
今回はクラスの継承や拡張についてまとめていきたいと思います。

# basic syntax
part1後の書き方の練習です。
```jsx
class Person {
  constructor(name, birthYear) {
    this.name = name
    this.birthYear = Number(birthYear)
  }

  calculateAge() {
    const today = new Date()
    return today.getFullYear() - this.birthYear
  }
}
let Taro = new Person('Taro', '1980')
console.log(Taro) // Person { name: 'Taro', birthYear: 1980 }
console.log(Taro.calculateAge())
```
インスタンスを創るのに、`constructor()`への引数に任して`this`オブジェクトを創り内部メソッドが`this`オブジェクトをアクセスして値を利用する書き方です。
```jsx
class Person {
  constructor(name) {
    this.name = name
  }

  calculateAge(birthYear) {
    this.birthYear = Number(birthYear)
    const today = new Date()
    return today.getFullYear() - this.birthYear
  }
}
let Taro = new Person('Taro')
console.log(Taro) // Person { name: 'Taro' }
console.log(Taro.calculateAge('1980')) // 42
console.log(Taro) // Person { name: 'Taro', birthYear: 1980 }
```
メソッド内部に`this`オブジェクトを作り、呼び出されるときに付与する引数が指定の値となり、それを利用する書き方です。
`calculateAge()`の中に`this.`でオブジェクトプロパティを生成させるから別の`this`オブジェクトが生成されると思ったけれど、実際はどこで`this`を使っても`class Person`内部フィールドで一つのコンテキストにまとめられているような感じです。

（まとめて`constructor()`に書く、必要に応じて各関数に書く、どちらがいいかわからないんですが、場合によって使い分けが必要だと思います。）

以下は`class`の`this`についての検証コード。
```jsx
class Test {
  constructor(a) {
    // console.log(this) // Test {}
    this.a = a
  }

  test1(a) {
    // console.log(this) // Test {}
    this.a = a
    console.log(this.a)
  }
}

let test = new Test(5)
console.log(test) // Test { a: 5 }
test.test1(10) // 10
console.log(test) // Test { a: 10 }
```
`class`フィールドにある`this`は全部一つのオブジェクトにまとめられ、プロパティ名が同じであれば上書きもできます。

# extends
`extends`は`class`を拡張させる機能を持ち、今のクラス宣言にほかのクラスの子クラス（派生クラス）を生成させるキーワードです。
```jsx
class Person {
  constructor(name) {
    this.name = name
  }

  calculateAge(birthYear) {
    this.birthYear = Number(birthYear)
    const today = new Date()
    return today.getFullYear() - this.birthYear
  }
}
//
class Taro extends Person {
  place(birthPlace) {
    this.birthPlace = birthPlace
    return `I come from ${this.birthPlace}`
  }
}

let taro = new Taro('Taro Yamada')
console.log(taro.calculateAge('1995')) // 27
console.log(taro.place('Japan')) // I come from Japan
console.log(taro)
// Taro { name: 'Taro Yamada', birthYear: 1995, birthPlace: 'Japan' }
```
part1の検証で、`constructor()`内部で定義されてない限り、メソッドたちは`prototype`へ移行されるという不思議な動きがあると分かりました。
自分の持っていないメソッドはプロトタイプチェーンで親`class`の`prototype`へたどり着き、メソッドをアクセスする。
例えば、`place()`は`Taro.prototype`、`calculateAge()`は`Person.prototype`を。

もう一度`Object.getPrototypeOf()`で検証してみると、
```jsx
console.log(Person instanceof Function) // true

console.log(Object.getPrototypeOf(taro) === Taro.prototype) // true
console.log(Object.getPrototypeOf(Taro.prototype) === Person.prototype) // true
console.log(Object.getPrototypeOf(Person.prototype) === Object.prototype) // true

function personFn() { }
let newPersonFn = new personFn()
console.log(Object.getPrototypeOf(newPersonFn) === personFn.prototype) // true
console.log(Object.getPrototypeOf(personFn.prototype) === Object.prototype) // true
```
`class`の`prototype`の継承方法はコンストラクタ関数とは変わらないのですが、
```jsx
console.log(Taro instanceof Function) // true

console.log(Object.getPrototypeOf(taro) === Taro.prototype) //true
console.log(Object.getPrototypeOf(Taro) === Person) // true

console.log(Object.getPrototypeOf(newPersonFn) === personFn.prototype) // true
console.log(Object.getPrototypeOf(personFn) === Function.prototype) // true
```
インスタンスは自分の`prototype`持ってないため本来なら親の`prototype`からプロパティやメソッドをアクセスするのです。
`class Taro`も`Function`インスタンスだから、プロトタイプチェーンのルールからすれば`class Taro`の`.__proto__`、つまり一個上のプロトタイプチェーンは親の`Person.prototype`のはずだったけれど。

しかし`console.log(Object.getPrototypeOf(Taro) === Person) // true`っていうのは、やっぱりコンストラクタ関数と違います。これで`class`の`extends`継承は親の`prototype`ではなく、親のフィールドへの延長ってことが分かりました。

`class Taro extends Person`というのは、`class Taro`が`class Person`へのアクセスを許可する。

では`console.log(Object.getPrototypeOf(taro) === Taro.prototype) // true`はどういうことでしょうか。part1で検証した結果、`constructor()`内で定義されてないメソッドは`prototype`に移行される。
```jsx
console.log(Object.getOwnPropertyNames(Taro.prototype))
// [ 'constructor', 'place' ]

```
これがインスタンス`taro`が`Taro.prototype`へアクセスして、`place()`を利用できる理由だと思います。そして`class Person`が`class Taro`の延長フィールドとして、自分のインスタンス`taro`も`class Person`フィールドをアクセスできます。

# overriding class field

```jsx
class Taro extends Person {
  calculateAge(birthYear) {
    this.birthYear = Number(birthYear)
    return this.birthYear
  }
}

let taro = new Taro('Taro Yamada')
console.log(taro.calculateAge('1995')) // 1995
```
（ここからの部分は自分の解釈が多いです。）
子クラス`class Taro`内部のメソッドが、親クラスの`class Person`と同じ名前のメソッドが存在すれば上書きします（自フィールド）。という言い方は適切かどうかは分かりませんが。
もともと自分のフィールドにないものだから延長線上の親フィールドへたどり着きメソッドを利用するようになる。しかし今は自分のフィールドで見つけるようになったら、親フィールドへアクセスせず自フィールドのメソッドを利用するのではないかと。

実際、子クラスフィールド内で定義されたメソッドは親フィールドのメソッドを上書きするのではありません。
```jsx
class Taro extends Person {
  calculateAge(birthYear) {
    this.birthYear = Number(birthYear)
    return this.birthYear
  }
}

let taro = new Taro('Taro Yamada')
console.log(taro.calculateAge('1995')) // 1995

class Jiro extends Person { }

let jiro = new Jiro('Jiro Yamada')
console.log(jiro.calculateAge('1999')) // 23
```
別の子クラスフィールド内部は同じ名前のメソッドを定義しなければまた親フィールドにたどり着きます。

`this`の作用が大事だと感じて、`class`フィールドと`constructor()`での違いがとても気になるので、まずは
```jsx
// class field
class Person {
  name = 'Taro'
}

class Taro extends Person { }

let taro = new Taro()
console.log(taro.name)
console.log(Object.getOwnPropertyNames(taro)) // [ 'name' ]
console.log(taro.hasOwnProperty('name')) // true

// constructor
class Person {
  constructor(name) {
    this.name = name
  }
}

class Taro extends Person { }

let taro = new Taro('Taro')
console.log(taro.name)
console.log(Object.getOwnPropertyNames(taro)) // [ 'name' ]
console.log(taro.hasOwnProperty('name')) // true
```
どちらでもインスタンス`taro`が所有していて、しかし
```jsx
class Person {
  name = 'Taro'

  constructor() {
    this.name = 'Jiro'
  }
}

class Jiro extends Person { }
let jiro = new Jiro()

console.log(jiro) // Jiro { name: 'Jiro' }
```
`constructor()`に`this`で同じプロパティを再定義したら、`class`フィールドのプロパティをオーバーライドします。

ほかの例も一緒に見ていきたいと思います。
```jsx
class Person {
  test = function fnInClassField() {
    console.log('class field test')
  }

  constructor() {
    console.log(this)
    // Jiro { test: [Function: fnInClassField] }

    this.test = function fnInConstructor() {
      console.log('function in constructor')
    }
  }

  test() {
    console.log('function in parent class')
  }
}

class Jiro extends Person { }
let jiro = new Jiro()

console.log(jiro)
// Jiro { test: [Function: fnInConstructor] }
```
同じ名前`test`プロパティを三つのところに用意して、`constructor()`内部で`this`オブジェクトをログしてみたら、最初の状態では`Jiro { test: [Function: fnInClassField] }`だったが、下にある`this.test`に上書きされ、最終的に`Jiro { test: [Function: fnInConstructor] }`になりました。

では`test()`はどこにいってました？
```jsx
class Person {
  test() {
    console.log('function in parent class')
  }
}

class Jiro extends Person { }

let jiro = new Jiro()
console.log(jiro)
// Jiro {}
console.log(jiro.hasOwnProperty('test'))
// false
console.log(Object.getOwnPropertyNames(Person.prototype))
// [ 'constructor', 'test' ]
```
`Person.prototype`ですね。

そして子クラスでは同じ名前のメソッドが書かれても、
```jsx
class Jiro extends Person {
  test() {
    console.log('function in child class')
  }
}

let jiro = new Jiro()
jiro.test()
// function in constructor
console.log(jiro.test)
// [Function: fnInConstructor]

console.log(Object.getOwnPropertyNames(Jiro.prototype))
// [ 'constructor', 'test' ]
```
**`class Person`の`constructor()`を上書きしません。**
`class Jiro`内部の`test()`もさきほどのルールのように自分の`prototype`へ移行しました。
`console.log(jiro.test)`呼び出しは、`class Person`の`constructor()`に一番さきにたどり着きました。

さらに、もし`constructor()`のプロパティ`test`を消して、どちらがさきにたどり着くのかな？
```jsx
jiro.test() // class field test
console.log(jiro.test) // [Function: fnInClassField]
```

`class Person`のプロパティ`test`もなかったら、
```jsx
jiro.test() // function in child class
console.log(jiro.test) // [Function: test]
```
`Jiro.prototype`ですね。

**となると、アクセスの優先順位は、
親`constructor()`　→　親フィールド　→　自分の`prototype`　→　親の`prototype`**


ここまで少しまとめてみたいと思います。なぜか`class`は`typeof`では`function`であったが、内部の構造としてはオブジェクトに近いと感じています。

* `class`フィールドで書いたプロパティが`constructor()`によって最終的に上書きされたり、上書きされずに`this`オブジェクトのプロパティに残ります。
* メソッドは変数で保存されてなかったら、`this`オブジェクトではなく`.prototype`へ。
* 上の二点によってアクセス順位が付けられています。
* `constructor()`のプロパティには`this`が必須で、インスタンスの`this`オブジェクトを生成するためにも、外部から引数の導入先を指定するためにも`this`に頼っています。

# super
`super`キーワードの使い方としてですが、
* 子クラスの`contructor()`で`this`を使う前に`super()`呼び出さねばなりません。
* 子クラスのないメソッドなら、`super.`を通して親クラスのメソッドを使えるようになります。

下は子クラスに`super()`を経由して親フィールドへ
```jsx
class Person {
  constructor(name) {
    this.name = name
    console.log(this)
  }
}

class Taro extends Person {
  constructor(birthYear, birthPlace) {
    super()
    this.birthYear = birthYear
    this.birthPlace = birthPlace
    console.log(this)
  }
}

let taro = new Taro('1995', 'Tokyo')
console.log(taro.name) // undefined
console.log(Object.getOwnPropertyNames(taro)) // [ 'name', 'birthYear', 'birthPlace' ]
console.log(taro.hasOwnProperty('birthYear')) // true
console.log(taro.hasOwnProperty('birthPlace')) // true
```

```jsx
class Person {
  constructor(name) {
    this.name = name
  }

  calculateAge(birthYear) {
    this.birthYear = birthYear
    const today = new Date()
    return today.getFullYear() - this.birthYear
  }

  place(birthPlace) {
    this.birthPlace = birthPlace
    return `${this.name} comes from ${this.birthPlace}`
  }
}
// super
class Taro extends Person {
  personalInfo(birthYear, birthPlace) {
    setTimeout(() => console.log(`${this.name} is ${super.calculateAge(birthYear)} years old, and ${super.place(birthPlace)}.`), 1000)
  }
}

let taro = new Taro('Taro Yamada')

console.log(taro.name) // Taro Yamada
console.log(taro.calculateAge('1995')) // 27
console.log(taro.place('Japan')) // Taro Yamada comes from Japan
taro.personalInfo('1995', 'Japan')
// Taro Yamada is 27 years old, and Taro Yamada comes from Japan.
```
正直に言って、ここまでの検証から見ると`super`キーワードは`this`の参照先の示しみたいな機能を持つものだと感じています...自分の`constructor`を再定義する前に`super()`で`this`の参照を一致させる仕事をしているみたいな感じです。（じゃないと親`constructor`で定義されたプロパティとメソッドが使えなくなる。）

# use Arrow functions
参考文章の[Class inheritance](https://javascript.info/class-inheritance)ではちょっと気になるところがありますので、
```jsx
class Taro extends Person {
  personalInfo(birthYear, birthPlace) {
    setTimeout(() => {
      console.log(super.calculateAge(birthYear))
      console.log(super.place(birthPlace))
    }, 1000)
  }
}

let taro = new Taro('Taro Yamada')
taro.personalInfo('1995', 'Japan')
// 27
// Taro Yamada comes from Japan
```
`Web APIs`などの外部関数なら、そのなかの`callback`はアロー関数のように書いても構いません、ちゃんと動けます。むしろアロー関数じゃなきゃ動けなくなる。
```jsx
class Taro extends Person {
  personalInfo() {
    setTimeout(() => console.log(this), 1000)
  }
}

let taro = new Taro('Taro Yamada')
taro.personalInfo()
// Taro { name: 'Taro Yamada' }
```

```jsx
class Taro extends Person {
  personalInfo() {
    setTimeout(function () {
      console.log(this)
    }, 1000)
  }
}
let taro = new Taro('Taro Yamada')
taro.personalInfo()
// Timeout {
//   ...
// }
```
[JavaScriptのthisについて -- this in Arrow function](https://qiita.com/hu-yu/items/74529effd585de1d70ca#this-in-arrow-function)
[JavaScriptのthisについて -- API asynchronous callbacks](https://qiita.com/hu-yu/items/74529effd585de1d70ca#api-asynchronous-callbacks)
やはり問題なのは`this`です。アロー関数なら`this`は親のコンテキストを継承するので、ここの親フィールドは`Taro {}`だから`this`コンテキストもそれを継承し、ちゃんとプロパティとメソッドをアクセスできる状態です。
`Web APIs`の`callback`がアロー関数じゃなければ、`this`コンテキストは普通は`window`に参照していますが、ここは`Timeout {}`自体を参照する、なんか興味深いことですが、ここまでの知識がないためまず課題として残しておきます。

下はクロージャの検証です。

```jsx
class Saburo extends Person {
  add(a) {
    return (b) => {
      console.log(this)
      return a + b
    }
  }
}
let saburo = new Saburo()
console.log(saburo.add(1)(2))
// Saburo {}
// 3
```
```jsx
class Saburo extends Person {
  add(a) {
    return function (b) {
      console.log(this)
      return a + b
    }
  }
}

let saburo = new Saburo()
console.log(saburo.add(1)(2))
// undefined
// 3
```
当たり前かもしれないけど、クロージャでもアロー関数じゃないと`this`の参照が変わってしまう。
（使えるは使えるけれど、`this`にかかわるプロパティは使えませんが）

```jsx
class Saburo extends Person {
  constructor(a, b) {
    super()
    this.a = a
    this.b = b
  }
  add() {
    return () => {
      console.log(this)
      return this.a + this.b
    }
  }
}
let saburo = new Saburo(1, 2)
console.log(saburo.add()())
// Saburo { a: 1, b: 2 }
// 3
```
（でも多分、たいていのプロはこういう書き方はしないと思います...。）

`Web APIs`ではなくても、なるべく`this`コンテキストを一致するため、メソッド内部の関数はアロー関数の書き方を使った方がいいと思います。

# super & [[HomeObject]]
あまり使ったことのない`super`に出会って、最初`super`が`this`を子クラスである自フィールドへ変えたのでしょうか？という素朴な疑問持ってたけれど、いろいろ調べてわかったこと、以下は自分で解釈しました。

[[[Super: internals, [[HomeObject]]]]](https://ja.javascript.info/class-inheritance#ref-148)
[[[HomeObject]]](https://ja.javascript.info/class-inheritance#ref-149)

**自分のオブジェクトで定義された`this`コンテキストを、ほかのオブジェクトを経由して参照先を再指定することができません。**

前`this`について色々と調べたから、`class`をオブジェクトとして見ると、内部のメソッド（`constructor()`もメソッドも）が**Implicit binding**`this`は一個上のコンテキストつまり`class`を指しています。そうなると親クラスも子クラスも自分の`this`を持ち、子クラスが延長線のままでは`this`を使うという自フィールドに新しく定義しようとする行為はエラーになってしまいます。（`this`は自フィールドなのに、メソッドは親フィールド。じゃどっちを参照すればいい？って混乱しちゃうよね）

なので`super`を通して`this`を定義する（その前`this`はuninitialized）、子クラス`this`が自フィールドに指してもらうというのは、`super`が`this`の参照を親フィールドでも自フィールドでもなく、**[[HomeObject]]** プロパティの値を参照せよって指示を出したわけだと思います。そして`constructor()`内で`super()`を使えばそれ以降の`this`の参照が確定して、もし親フィールドのメソッドをアクセスしようとするなら`super.`を通してメソッドを**そのまま**使わせてもらう。
（`super.`で`this`コンテキストを上書きするのではなく、`super.`でメソッドをコピーして自フィールドに持ち込むという意味です。`super`は`this`に **[[HomeObject]]** へ参照させ、次第にコピーされたメソッドは自フィールドのプロパティを使うようになる）

[How to extend a class without having to use super in ES6? - stackoverflow](https://stackoverflow.com/questions/31067368/how-to-extend-a-class-without-having-to-use-super-in-es6)

```jsx
class Person {
  constructor(birthPlace = 'America') {
    this.name = 'Saburo'
    this.birthYear = 2000
    this.birthPlace = birthPlace
  }

  test() {
    console.log(`I am ${this.name}, I was born on ${this.birthYear} in ${this.birthPlace}`)
  }
}

class Saburo extends Person {
  constructor(birthPlace) {
    // name = 'Saburo'
    // ReferenceError: name is not defined
    // this.name = 'Saburo'
    // ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor
    super()
    this.birthPlace = birthPlace
  }
}

let saburo = new Saburo('Hokkaidou')
saburo.test()
// I am Saburo, I was born on 2000 in Hokkaidou

//
class Shiro extends Person { }
let shiro = new Shiro()
shiro.test()
// I am Saburo, I was born on 2000 in America
```
`this`はuninitialized状態だと、`ReferenceError`参照先エラーで、自フィールドにないプロパティが`not defined`になったり、自フィールドの`this`をアクセスする前に`super`の呼び出しが必須になったりする。
`saburo.test()`で`name`を出力できるのは決して自フィールドに`name`を持ってるわけではなく、親フィールドにアクセスしてプロパティの値を参照し、`birthPlace`を変えたのも、決して親フィールドのプロパティを変えたのでなく、super()で`this`が確定し、自フィールドの`this`オブジェクトに`birthPlace`を見つかったからさらに親フィールドへアクセスする必要もなくなった。

自分の解釈や推測が多いけれど何だか前の検証からも納得できる結論に導いている。次回は静的プロパティとメソッドをまとめていきたいと思います。