【TypeScript/JavaScript】__extendsやnew __()は何をやっているか(What is __extends and new __ () doing?)

【TypeScript/JavaScript】__extendsやnew __()は何をやっているか(What is __extends and new __ () doing?)

TypeScriptでスーパークラスを継承した場合、書き出されるjsの中に_extends()という箇所があります。

【TypeScript】__extendsが何をやっているか

【TypeScript】__extendsが何をやっているか

これが何をしているのかわかり易いgitBookがあったのでシェアします。

https://basarat.gitbooks.io/typescript/content/docs/classes-emit.html

この記事はTypeScriptでextendsした場合どのような処理で継承しているのか、書き出される_extendsの動きを理解する記事です。

gitBook先ではPointを親クラスにして、子クラスのPoint3Dがそれを継承したら書き出されるJSの説明をしています。

上記リンクは継承を省いているので同様の実装をしてみました。(型定義は本質ではないので適当)
これが元ts

結果、
書き出されるJSはこちら

ここの中にある__extends()とnew __()。

みやすくするためにコードにコメントしました。(同じコードです。)

・_extends(d, b)は
子クラス(d)と親クラス(b)を引数として子クラス定義時一番先に実行し、
・dのプロパティにbの親クラスが持つ静的メンバをコピー、
・親のprototype上のインスタンスメソッドをdが使えるように設定している。

このコピーとprototypeを遡れるようにすることがJSでいう継承。

ここで説明を終えてもいいのですが、
__proto__とprototypeが何をしているかを理解することは重要です。

gitBookでは
やっていることは
d.prototype.__proto__ = b.prototype
だとしていて、
これを理解するには

・__proto__
・prototype
・effect of new on this inside the called function
・effect of new on prototype and __proto__

これらを理解するのが重要だとしています。
こちらもわかりやすかったです。

要約すると、というか全訳しています。
覚えておかなくてはいけないのは、

__proto__

・全てのオブジェクトは__proto__を持つ。
・例えばobj.nameを参照時、nameを自身のプロパティとして持っていない場合、__proto__を辿る(継承先を)。最終地点まで辿って「見つけられない」場合undefinedを返す。

もう一方で

prototype

・全てのfunctionはprototypeを持っていて、prototypeは自分自身へのfunctionを返すconstructorを持つ
・つまりFoo.prototype.constructor === Foo//true
・関数のprototypeが、関数呼び出し(new Foo())から返された新しく作成されたオブジェクトの__proto__にコピーされます

これを踏まえて、
上記のコードでのこの3ラインがありました。

これは逆から処理を追えばわかり易いようです。

3のやっていることは、

d.prototype = new __()
つまり、
d.prototype = {__proto__ : __.prototype}

{__proto__: __.prototype}の部分は、

「__が持つprototypeを新しく作成されたObjectの__proto__上に持ち、それを子のprototypeに参照させている。」

ということをしていて

それより前の行。
2で、
__.prototype = b.prototypeをしているため
結果、
d.prototype = {__proto__ : b.prototype}
をしていることになる。
親のprototypeを__proto__経由でd.prototypeから辿れるようにしている。
二回同じこと言っています。

さて、
d.prototype .__ proto__は望み通りになったが、
「proto上」が変更されただけで、
古い、d.prototype.constructorは維持されています。(子が持つ自分自身への関数参照)
これが、
最初の行(つまり、__(){this.constructor = d;})の意味になります。
d自身のconstructorの「修繕」です。 (prototype上のそれが置き換わってしまうため)

なので実質やっていることは
d.prototype = {__proto__:__.prototype、d.constructor = d}

このd.prototype.__proto__ = b.prototypeが重要で、

これが__extends()の中でやっていることです。

これを理解した上で、
ES5記述の下記prototype継承をみると何しているかわかり易いです。

コメントアウトの箇所。
なんか、今までここのコードを見ていた過去の自分と、別人になれた気がします。
皆さんもこれが何しているのか説明できると思います笑

__proto__とprototyhpeの違いについては過去に書きました。

__extends()が何しているか、new __()が何しているか、
わかり易い記事に出会えて良かったです。

ではみなさま楽しいprototype継承生活を〜〜