Pourquoi en JavaScript est (super .__ proto__ === ce .__ proto__) vrai?

10

Il semble que dans les classes JavaScript (ES6) super.__proto__ === this.__proto__.

Pouvez-vous expliquer pourquoi c'est le cas? Le comportement semble cohérent entre les différents navigateurs, donc je soupçonne que cela est spécifié quelque part dans la spécification.

Considérez le code suivant:

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();

Je me serais attendu à ce que super.__proto__.myFunc();cela appelle la fonction myFunc()de classe Level1et cela super.__proto__ !== this.__proto__. Au lieu de cela, il super.__proto__.myFunc();appelle en fait la myFunc()classe Level3(il s'appelle lui-même), puis lors de la deuxième invocation, il appelle la myFunc()classe Level2. Ceci est parfaitement compréhensible si super.__proto__ === this.__proto__le code le démontre.

Pouvez-vous expliquer la raison pour laquelle super.__proto__ === this.__proto__dans cet exemple? Si possible, veuillez également fournir des références à la section pertinente de la spécification.

Jens Moser
la source

Réponses:

6

Object.prototype.__proto__est une propriété avec un getter [1] . Il fonctionne sur sa thisvaleur. Il n'y a pas réelle superobjet à une thisvaleur (vous ne pouviez pas écrire Object.getPrototypeOf(super)), juste une superfaçon de regarder des propriétés, donc this.__proto__et super.__proto__dire la même chose, tant que __proto__n'est pas aussi défini nulle part plus faible de la chaîne de prototype.

Comparer:

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-
la source
Je soupçonnais déjà que cela avait quelque chose à voir avec le __proto__fait d'être des fonctions d'accesseur Object.prototypeet de fonctionner sur leur thisvaleur. Mais je ne pouvais tout simplement pas imaginer que cela superétait réellement spécifié pour fonctionner de cette façon. Je pensais superêtre à peu près équivalent à this.__proto__.__proto__, donc super.__proto__aurait été équivalent à this.__proto__.__proto__.__proto__ce qui aurait montré le comportement que j'attendais. Savez-vous où, dans la spécification, le comportement exact de superest spécifié?
Jens Moser
@JensMoser: Je le trouverai dans un peu, mais imaginez des utilisations normales de super, comme super.setFoo('bar'). Vous ne voudriez pas que cela fonctionne sur un prototype au lieu de l'instance.
Ry-
@georg Je sais que __proto__c'est une propriété d'accesseur sur Object.prototype. Lorsque j'ai demandé une référence à la spécification, je voulais dire une référence au comportement exact du supermot clé en conjonction avec __proto__. Voir mon commentaire précédent.
Jens Moser
@ Ry- Oui, j'ai simplifié un peu. Ma compréhension exacte super.setFoo('bar')serait, qu'il est équivalent à this.__proto__.__proto__.setFoo.call(this, 'bar'). Donc, superappelle automatiquement les fonctions avec la bonne this.
Jens Moser
1
@JensMoser super.__proto__(dans cette méthode de la Level3classe) est exactement équivalent àReflect.get(Object.getPrototypeOf(Level3.prototype), "__proto__", this)
Bergi