クラスの継承をたどるコード

《 初回公開: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クラスを継承している事が確認できる。

ページのトップへ戻る