クラスの継承をたどるコード
《 初回公開:2024/02/15 , 最終更新:未 》
【 目次 】
前回の「 クラスの継承のメカニズム - プロトタイプチェイン」
の記事では、内容が長くなってしまいごちゃごちゃしてわかりづらくなってしまったので、「クラスの継承関係をたどるコード」に的を絞って前回の記事を整理し直してみる。
内容が重複してしまうが。
前提条件
以下のようにクラスの継承関係が定義されていたとして。
class SuperCls { }
class MyCls extends SuperCls { }
class SubCls extends MyCls { }
インスタンスはconstructorプロパティを持ち、その値はインスタンスの属するクラスを指す。
let myObj = new MyCls();
let myCls = myObj.constructor;
console.log(myCls === MyCls); // true
クラスはクラス名を示すnameプロパティを持つ。
console.log(myCls.name); // MyCls
従ってインスタンスの属するクラス名は通常、[インスタンス].constructor.nameで得る事ができる。
console.log(myObj.constructor.name); // MyCls
インスタンスは__proto__プロパティを持ち、その値はそのインスタンスが属するクラスのprototypeプロパティを指す。
逆に言うと、__proto__プロパティの値がクラスのprototypeプロパティを指すという事は、そのクラスのインスタンスと言える。
console.log(myObj.__proto__ === MyCls.prototype); // true
更にクラスのprototypeプロパティはスーパクラスのインスタンス。
console.log(MyCls.prototype instanceof SuperCls); // true
console.log(MyCls.prototype instanceof MyCls); // false
既に述べたとおりインスタンスの属するクラスのクラス名は通常、[インスタンス].constructor.nameで得る事ができる。
しかし、クラスのprototypeプロパティはスーパクラスのインスタンスであるが、このprototypeのconstructorプロパティは自分自身を指すように差し替えられていて。
console.log(MyCls.prototype.constructor.name); // MyCls
インスタンスは__proto__プロパティの更に__proto__プロパティはそのインスタンスが属するクラスのスーパクラスのprototypeプロパティを指している。
console.log(myObj.__proto__.__proto__ === SuperCls.prototype); // true
console.log(myObj.__proto__.__proto__.constructor.name); // SuperCls
インスタンスの属するクラスの継承関係をたどるコード
インスタンスの__proto__プロパティをたどる事でインスタンスの継承関係を調べる事ができる。
console.log(myObj.__proto__.__proto__.__proto__ === Object.prototype); // true
console.log(myObj.__proto__.__proto__.__proto__.constructor.name); // Object
__proto__プロパティをたどると最後(Object.prototype)はnullに
console.log(Object.prototype.__proto__ === null); // true
以上の事からインスタンスからインスタンスの属するクラスの継承関係をたどるコードを導き出すと
インスタンスからインスタンスの属するクラスの継承関係をたどるコード
let o = new SubCls();
let s = "";
o = o.__proto__;
while (o) {
if (s) {
s += "=>"
}
s += o.constructor.name;
o = o.__proto__;
}
console.log(s); // SubCls=>MyCls=>SuperCls=>Object
クラスからクラスの継承関係をたどるコード
インスタンスと同様にクラスも__proto__プロパティを持つが、インスタンスと異なりその値はクラスのスーパークラスを指す。
console.log(SubCls.name); // SubCls
console.log(SubCls.__proto__.name); // MyCls
console.log(SubCls.__proto__.__proto__.name); // SuperCls
基底クラスSuperClsの__proto__をたどるとFunction.prototypeを経てObject.prototype,そしてその先はnullに
console.log(SuperCls.__proto__ === Function.prototype); // true
console.log(SuperCls.__proto__.__proto__ === Object.prototype); // true
console.log(SuperCls.__proto__.__proto__ === null); // true
クラスからクラスの継承関係をたどると
クラスからクラスの継承関係をたどるコード
let s = "", c = SubCls;
while (c != null) {
if (s) {
s += "=>"
}
if (c === Function.prototype || c === Object.prototype) {
s += `${c.constructor.name}.prototype`
} else {
s += c.name;
}
c = Object.getPrototypeOf(c); // c = c.__proto__
}
console.log(s); // SubCls=>MyCls=>SuperCls=>Function.prototype=>Object.prototype
ここで、SuperClsから先がFunction.prototypeやObject.prototypeになっている事に違和感を憶える事と思うが
console.log(SuperCls.__proto__ === Function.prototype); // true
SuperCls.__proto__がFunction.prototypeという事は基底クラスSuperClsがクラスであると同時にFunctionクラスのインスタンスになっている事を示している。 (__proto__プロパティの値がクラスのprototypeプロパティを指すという事は、そのクラスのインスタンス)
同じように
console.log(Function.prototype.__proto__ === Object.prototype); // true
Function.prototype.__proto__がObject.prototypeという事は、Functionクラスのインスタンスの継承元がObjectクラスのインスタンスになっている事を示している。
更にその先のは
console.log(Object.prototype.__proto__ === null); // true
Objectクラスのインスタンスは継承元が存在していないことを示している。
組み込みクラスの継承関係
組み込みクラスの継承においてもこれらのコードは有効で、Fileオブジェクトからの継承関係をたどってみると。
var o = new File(["foo"], "foo.txt");
let s = "";
o = o.__proto__;
while (o) {
if (s) {
s += "=>"
}
s += o.constructor.name;
o = o.__proto__;
}
console.log(s); // File=>Blob=>Object
FileオブジェクトがBlobクラスを継承している事が確認できる。