ES2015(ES6)の子クラスの constructor() では super() が必須で、super()の前には this.* や super.* が使えない話


## TL;DR

タイトルのままです。

## 背景：よく出会うエラー

1. `SyntaxError: missing super() call in constructor`
2. `SyntaxError: 'this' is not allowed before super()`
3. `SyntaxError: 'super.*' is not allowed before super()`

## 1. 子クラスの`constructor()`では`super()`呼び出しが必須

### エラーになるコード例

こういうコードはエラーになります。

```js:missing-super-error.js
class Parent {
}

class Child extends Parent {
  constructor() {}
}
```

### 解決･解消コード例

`super()`を追加します。

```js:fix-missing-super-error.js
class Parent {
}

class Child extends Parent {
  constructor() { super(); }
}
```

または特にコンストラクタで何もしないなら、`constructor()`自体を削除してしまいましょう。

```js:fix-missing-super-error.js
class Parent {
}

class Child extends Parent {
}
```

これは、デフォルトで以下のように書いたのと同じです。

```js:default-subclass-constructor.js
class Child extends Parent {
  constructor(...args) { super(...args); }
}
```

ちなみにこのコードは、`new Child()`したときの引数をそのまま親クラスのコンストラクタへ引数として渡します。

## 2. 子クラスの`constructor()`では`super()`の前で`this`を参照することはできない

### エラーになるコード例

こういうコードは、どの子クラス定義でもエラーになります。

```js:this-before-super-error.js
class Parent {}

class Child01 extends Parent {
  constructor(params) {
    // super()の前で自プロパティへ代入
    this.params = params;
    super();
  }
}

class Child02 extends Parent {
  constructor(params) {
    // super()の前で自クラスのメソッドを呼ぶ
    const processed_params = this.preprocessing(params);
    super(processed_params);
  }

  preprocessing(params) {
    // process something
  }
}
```

### 解決･解消コード例

基本的には`super()`呼び出しより後に`this.*`を移動させると良いでしょう。

```js:fix-this-before-supper-error.js
class Child01 extends Parent {
  constructor(params) {
    super();
    this.params = params;
  }
}
```

`Child02`クラスのように、自クラスのメソッドの結果を`super()`に渡している場合は前後入れ換えで解決しませんが、このようなケースではクラス設計に間違いがある場合がほとんどです。

```js:fix-this-before-supper-error.js
class Child02 extends Parent {
  constructor(params) {
    super(params);
    this.preprocessing();
  }
}
```

上記のように引数をそのまま渡して`super()`を呼び、その後に自インスタンスを加工するという順番にできないか検討してみましょう。

ちなみに、`super()`の戻り値と、`super()`の後で参照できる`this`は同じものです。

```js:compare-instance-with-this.js
class Parent {}

class Child02 extends Parent {
  constructor() {
    const instance = super();
    console.log(instance === this); // => true
  }
}

new Child02();
```


## 3. 子クラスの`constructor()`では`super()`の前で`super.*`（親クラス呼び出し）はできない

### エラーになるコード例

こういうコードは、どの子クラス定義でもエラーになります。

```js:super-before-super-error.js
class Parent {
  processing(params) {
    // process something
  }
  get name() { return 'NAME'; }
}

class Child01 extends Parent {
  constructor(params) {
    // 親クラスのプロパティを参照する
    const name = super.name + params;
    super();
  }
}

class Child02 extends Parent {
  constructor(params) {
    // 親クラスのメソッドを呼ぶ
    const processed_params = super.processing(params);
    super(processed_params);
  }
}
```

### 解決･解消コード例

これらも`this`のときと同様、基本的には`super()`を先に呼ぶようにします。

```js:fix-super-before-super-error.js
class Child01 extends Parent {
  constructor(params) {
    super();
    const name = super.name + params;
  }
}
```

`Child02`のように親クラスのメソッドの実行結果を`super()`の引数として渡している場合は、`this`のときと同じようにクラス設計を見直して、親クラスの`constructor()`の方に`processing()`実行を移動させてしまうのが良いでしょう。

```js:fix-super-before-super-error.js
class Parent {
  constructor(params) {
    this.processed_params = this.processing(params);
  }
  processing(params) {
    // process something
  }
}

class Child02 extends Parent {
  constructor(params) {
    super(params);
	this.processed_params
  }
}
```

以上です。役に立ちそうでしたら「ストック」してもらえると嬉しいです。
それでは、良いES6ライフをお過ごしください。