【TypeScript/JavaScript】__extendsやnew __()は何をやっているか(What is __extends and new __ () doing?)
TypeScriptでスーパークラスを継承した場合、書き出されるjsの中に_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継承生活を〜〜