Warum ist in JavaScript (super .__ proto__ === this .__ proto__) wahr?

10

Es scheint, dass in JavaScript (ES6) Klassen super.__proto__ === this.__proto__.

Können Sie erklären, warum dies der Fall ist? Das Verhalten scheint in verschiedenen Browsern konsistent zu sein, daher vermute ich, dass dies irgendwo in der Spezifikation angegeben ist.

Betrachten Sie den folgenden Code:

class Level1 {
    myFunc() {
        console.log('Level1');
    }
}

class Level2 extends Level1 {
    myFunc() {
        console.log('Level2');
    }
}

class Level3 extends Level2 {
    myFunc() {
        console.log('Level3 BEGIN ' + Math.random()); 
        super.__proto__.myFunc();
        console.log(super.__proto__ === this.__proto__);
        console.log('Level3 END'); 
    }
}

const foo = new Level3();
foo.myFunc();

Ich hätte erwartet, dass super.__proto__.myFunc();das die Funktion myFunc()der Klasse nennen würde Level1und das super.__proto__ !== this.__proto__. Stattdessen super.__proto__.myFunc();ruft tatsächlich die myFunc()Klasse auf Level3(es ruft sich selbst auf) und beim zweiten Aufruf ruft es die myFunc()Klasse auf Level2. Dies ist durchaus verständlich, wenn super.__proto__ === this.__proto__der Code dies demonstriert.

Können Sie den Grund dafür super.__proto__ === this.__proto__in diesem Beispiel erklären ? Wenn möglich, geben Sie bitte auch Verweise auf den entsprechenden Abschnitt der Spezifikation an.

Jens Moser
quelle

Antworten:

6

Object.prototype.__proto__ist eine Eigenschaft mit einem Getter [1] . Es arbeitet mit seinem thisWert. Es gibt keine eigentliche superAufgabe , eine sein thisWertes (man kann nicht schreiben Object.getPrototypeOf(super)), nur eine superArt und Weise des Nachschlagen Eigenschaften, so this.__proto__und super.__proto__das Gleiche bedeuten, solange __proto__nicht auch irgendwo weiter unten auf der Prototypkette definiert ist.

Vergleichen Sie:

class Parent {
    get notProto() {
        return this instanceof Child;
    }
}

class Child extends Parent {
    test() {
        console.log(super.notProto);
    }
}

new Child().test();

// bonus: [1]
console.log(Object.getOwnPropertyDescriptor(Object.prototype, '__proto__'));

Ry-
quelle
Ich hatte bereits den Verdacht, dass dies etwas damit zu tun hat, __proto__dass Accessor-Funktionen tatsächlich Object.prototypeauf ihrem thisWert arbeiten und daran arbeiten . Aber ich kann mir einfach nicht vorstellen, dass dies supertatsächlich so funktioniert. Ich dachte super, ungefähr gleichwertig zu sein this.__proto__.__proto__, also super.__proto__wäre es gleichwertig gewesen, this.__proto__.__proto__.__proto__was das erwartete Verhalten gezeigt hätte. Wissen Sie, wo in der Spezifikation das genaue Verhalten von superangegeben ist?
Jens Moser
@JensMoser: Ich werde es in Kürze finden, aber stellen Sie sich normale Verwendungen von super, wie super.setFoo('bar'). Sie möchten nicht, dass dies auf einem Prototyp anstelle der Instanz ausgeführt wird.
Ry-
@georg Ich weiß, dass dies __proto__eine Accessor-Eigenschaft ist Object.prototype. Als ich nach einem Verweis auf die Spezifikation fragte, meinte ich einen Verweis auf das genaue Verhalten des superSchlüsselworts in Verbindung mit __proto__. Siehe meinen vorherigen Kommentar.
Jens Moser
@ Ry- Ja, ich habe ein bisschen vereinfacht. Mein genaues Verständnis super.setFoo('bar')wäre, dass es gleichbedeutend ist mit this.__proto__.__proto__.setFoo.call(this, 'bar'). Ruft also superautomatisch Funktionen mit der richtigen auf this.
Jens Moser
1
@JensMoser super.__proto__(in dieser Methode der Level3Klasse) ist genau gleichbedeutend mitReflect.get(Object.getPrototypeOf(Level3.prototype), "__proto__", this)
Bergi