¿Por qué en JavaScript es (super .__ proto__ === this .__ proto__) verdadero?

10

Parece que en JavaScript (ES6) Clases super.__proto__ === this.__proto__.

¿Puedes explicar por qué este es el caso? El comportamiento parece consistente en diferentes navegadores, por lo que sospecho que esto se especifica en algún lugar de la especificación.

Considere el siguiente código:

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

Hubiera esperado que eso super.__proto__.myFunc();llamara la función myFunc()de clase Level1y eso super.__proto__ !== this.__proto__. En cambio, en super.__proto__.myFunc();realidad llama myFunc()a la clase Level3(se llama a sí mismo) y luego, en la segunda invocación, llama myFunc()a la clase Level2. Esto es perfectamente comprensible si super.__proto__ === this.__proto__el código lo demuestra.

¿Puedes explicar la razón por qué super.__proto__ === this.__proto__en este ejemplo? Si es posible, proporcione también referencias a la sección relevante de la especificación.

Jens Moser
fuente

Respuestas:

6

Object.prototype.__proto__es una propiedad con un captador [1] . Opera en su thisvalor. No hay una real superobjeto a ser un thisvalor (que no podía escribir Object.getPrototypeOf(super)), sólo una supermanera de buscar propiedades, por lo que this.__proto__y super.__proto__significa lo mismo, siempre y cuando __proto__no se define también en cualquier lugar más abajo en la cadena de prototipo.

Comparar:

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-
fuente
Ya sospechaba que esto tenía algo que ver con __proto__estar realmente en funciones de acceso Object.prototypey operar en su thisvalor. Pero no podía imaginar que en superrealidad se especificara que funcionara de esta manera. Pensé en superser más o menos equivalente a this.__proto__.__proto__, por super.__proto__lo que habría sido equivalente a this.__proto__.__proto__.__proto__lo que habría exhibido el comportamiento que esperaba. ¿Sabes en qué parte de la especificación se especifica el comportamiento exacto de super?
Jens Moser
@JensMoser: Lo encontraré en un momento, pero imagina usos normales de super, como super.setFoo('bar'). No querrás que opere en un prototipo en lugar de la instancia.
Ry-
@georg Sé que __proto__es una propiedad de acceso en Object.prototype. Cuando solicité una referencia a la especificación, quise decir una referencia al comportamiento exacto de la superpalabra clave en conjunto con __proto__. Ver mi comentario anterior.
Jens Moser
@ Ry- Sí, simplifiqué un poco. Mi comprensión exacta de super.setFoo('bar')sería, que es equivalente a this.__proto__.__proto__.setFoo.call(this, 'bar'). Entonces, superinvoca automáticamente las funciones con el correcto this.
Jens Moser
1
@JensMoser super.__proto__(en ese método de la Level3clase) es exactamente equivalente aReflect.get(Object.getPrototypeOf(Level3.prototype), "__proto__", this)
Bergi el