javascriptのsuper.__proto__の挙動がよくわからん

ワタル
先日親クラスの状況を調べようと思ってsuper.__proto__を使ったら結果が意味不明だったので報告です。

アキラ
superって親クラスのメソッド呼ぶときに使うやつだよね。

ワタル
それ。でも、親クラスって言っても、superが使われているクラスの親なのか、実行時のthisの親なのか知ってる?

アキラ
あれどっちだろ。

ワタル
やってみようか。

class Parent {
    f() {
        console.log('Parent.f();')
        console.log(this);
    }
}

class Child extends Parent {
    f() {
        super.f();
        console.log('Child.f();');
        console.log(this);
    }
}

class Parent2 {
    f() {
        console.log('Parent2.f();')
        console.log(this);
    }
}

class Child2 extends Parent2 {
    f() {
        super.f();
        console.log('Child2.f();');
        console.log(this);
    }
}

const c = new Child();
const c2 = new Child2();
const f = c.f.bind(c2);
f();

出力結果

Parent.f();
Child2 {}
Child.f();
Child2 {}
アキラ
thisがChild2のインスタンスで、super使って呼ばれてるのがParent.f()だから、superが書かれているクラスの親のメソッドを呼んでるね。thisの親じゃなくって。

ワタル
そう。MDNのリファレンスにも↓のように書いてあるんだけど、残念ながら2023-01現在、日本語版のページにはその部分翻訳されてないのよね。

Note that the reference of super is determined by the class or object literal super was declared in, not the object the method is called on. Therefore, unbinding or re-binding a method doesn't change the reference of super in it (although they do change the reference of this).
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/super

ワタル
でも、挙動もドキュメントも一致してるから、めでたしめでたしかと思ったらそうはいかなかったのよ。

アキラ

ワタル
superの挙動いろいろやってみてもちゃんと上の通りでいいんだけど、なぜかsuper.__proto__だけは挙動が違う。

class GrandParent {
}

class Parent extends GrandParent{
    f() {
        console.log('Parent.f();')
        console.log(this);
        console.log(super.__proto__);
        console.log(this.__proto__);
        console.log(this.__proto__ === super.__proto__);
    }
}

class Child extends Parent {
    f() {
        super.f();
        console.log('Child.f();');
        console.log(this);
        console.log(super.__proto__);
        console.log(this.__proto__);
        console.log(this.__proto__ === super.__proto__);
    }
}

class Parent2 {
    f() {
        console.log('Parent2.f();')
    }
}

class Child2 extends Parent2 {
    f() {
        super.f();
        console.log('Child2.f();');
    }
}

const c = new Child();
const c2 = new Child2();
const f = c.f.bind(c2);
f();

出力結果

Parent.f();
Child2 {}
Parent2 {constructor: ƒ, f: ƒ}
Parent2 {constructor: ƒ, f: ƒ}
true
Child.f();
Child2 {}
Parent2 {constructor: ƒ, f: ƒ}
Parent2 {constructor: ƒ, f: ƒ}
true
アキラ
なにこれ。思いっきりthisに引っ張られてるじゃん。

ワタル
そう。さらにどの階層かによらず単にsuper.__proto__は、this.__proto__と同一のものをさしてるようなんだよね。

ワタル
最初chromeでやってみてこうだったので、なんじゃこりゃと思ってfirefoxでもやってみたけど同じ。

アキラ
MDNの説明と違うようだけどなにこれ?

ワタル
わかんない。ネット調べたら同じ疑問持った人がいて、stack overflow で質問してて「そんなもんjsの仕様のどこに書いとんじゃい?!」って。

アキラ
回答付いてるけど会話が噛み合ってないね。これ。

ワタル
書いてないけど、質問してる人はjsの仕様を自分でいろいろ探したけれど見つからないから質問投稿してるっぽいし、僕も少し探したけれどやっぱり見つけられなかった。

アキラ
変な挙動だと思うけど、変わる可能性あるのかなぁ?

ワタル
無いでしょ。そもそも__proto__自体非推奨だから使うなって書いてあるし、互換性考えると放置だろうね。

アキラ
じゃ、この記事的には、super.__proto__は使わないほうがよさそうってことで。閉めましょうか。

ワタル
そだね。