JavaScript・TypeScriptのclassの取り扱いについて 糖衣構文とプロトタイプチェーン

2015年(ES6)からclass記法で実装できるようになりました。

prototype記法から逃れてずいぶん経ちます。

TypeScriptも当たり前ですがトレンドを追っているのでclass記法で表記することができます。ただ、オブジェクト指向のクラスと厳密には異なります。

classの取り扱いとprototypeチェーンをまとめておきたいと思います。

プロトタイプモデルの糖衣構文としてclass

今回はTypeScriptでclassを表記しています。

クラス記法、フィールド、コンストラクタ、そしてメソッドの設定です。

ここだけを見るとオブジェクト指向と似たような構文に見えます。

実際にコンパイルしてみるとどうなるでしょうか。

//コンパイル前 ts file
class Dog {
	private name:string
	constructor(name:string){
		this.name = name
	}
	howl(){
		console.log(`${this.name} howling at some people`)
	}
}

コンパイル後のソースがこちらになります。

//コンパイル後
var Dog = /** @class */ (function () {
    function Dog(name) {
        this.name = name;
    }
    Dog.prototype.howl = function () {
        console.log(this.name + " howling at some people");
    };
    return Dog;
}());

Dogは関数を持つメソッドとして表記されています。

あくまでclass記法は糖衣構文(読み書きのしやすさのために導入される書き方)であり、JavaScriptのプロトタイプモデルを理解していく必要があるのです。

 

ECMAScript 2015 で導入された JavaScript クラスは、JavaScript にすでにあるプロトタイプベース継承の糖衣構文です。クラス構文は、新しいオブジェクト指向継承モデルを JavaScript に導入しているわけではありません

JavaScript リファレンス クラス

プロトタイプチェーン

JavaScriptのclass記法はプロトタイプベースの糖衣構文であるということが分かりました。では、classとprototypeで異なる点はどこでしょうか。

下記はDogを継承してDachshundを設定しました。

Dachshundクラスは空の実装ですが、インスタンス生成時にnameを受け取っています。また、howlもDogの実装から継承されて動作しています。

この点をみるとオブジェクト指向にある継承は問題なく出来ていそうです。

//ts file
class Dog {
	private name:string
	constructor(name:string){
		this.name = name
	}
	howl(){
		console.log(`${this.name} howling at some people`)
	}
}

class Dachshund extends Dog{
}

let pochi = new Dachshund("POCHI")
pochi.howl();//POCHI howling at some people

次にこの継承元のオブジェクトのメソッドを変更したものを追記します。

Dog.prototype.howl以下が追記部分です。

スーパークラスのメソッドが変わっても、インスタンスのメソッドは変わらないと思いますが残念ながら変化していまいます。

これにはプロトタイプモデルの特徴であるプロトタイプチェーンが関わってきています。

// ts file
class Dog {
	private name:string
	constructor(name:string){
		this.name = name
	}
	howl(){
		console.log(`${this.name} howling at some people`)
	}
}

class Dachshund extends Dog{
}

let pochi = new Dachshund("POCHI")
pochi.howl();//POCHI howling at some people

Dog.prototype.howl = function(){
	console.log(`garr`)
}
pochi.howl();//garr

プロトタイプチェーンは、スーパークラスが存在しており、かつそのクラスに存在しないプロパティがある場合、スーパークラス側に参照が行くという動作を繰り返してチェーンのようになっている動きを指します。

今回でいえば、インスタンスにhowl()が存在していないため、スーパークラスであるDog内が参照され、howl()を呼んでいます。

参照されるという点がミソで、スーパークラスのメソッドを上書きしてしまうと、以降の呼び出しでは上書き後のメソッドが実行されてしまいます。

これが、pochi.howl();でgarrがログに出力されてしまう理由です。

まとめ JavaScriptのclass記法はオブジェクト指向のものとだいぶ違った動きをするよね

まとまりました。

JavaScriptのclass記法は糖衣構文なので、オブジェクト指向の動きを求めるのはちょっと違うなぁという感じです。

とはいえwebを触っている以上は避けることができないので適応していきましょう。継承の概念は実装の重複をなくしたり関数をラップして概念として取り扱うことができるので使いこなしていきたい。

bingo.web 2でもお待ちしています。

シェアする

  • このエントリーをはてなブックマークに追加

フォローする