Por que no JavaScript é (super .__ proto__ === this .__ proto__) verdadeiro?

10

Parece que nas classes JavaScript (ES6) super.__proto__ === this.__proto__.

Você pode explicar por que esse é o caso? O comportamento parece consistente em diferentes navegadores, então suspeito que isso esteja especificado em algum lugar da especificação.

Considere o seguinte 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();

Eu esperava que super.__proto__.myFunc();isso chamasse a função myFunc()de classe Level1e isso super.__proto__ !== this.__proto__. Em vez disso, super.__proto__.myFunc();na verdade, chama myFunc()de classe Level3(ela se chama) e, na segunda chamada, chama myFunc()de classe Level2. Isso é perfeitamente compreensível se super.__proto__ === this.__proto__o código demonstrar.

Você pode explicar o porquê super.__proto__ === this.__proto__neste exemplo? Se possível, forneça também referências à seção relevante da especificação.

Jens Moser
fonte

Respostas:

6

Object.prototype.__proto__é uma propriedade com um getter [1] . Opera em seu thisvalor. Não existe um superobjeto real para ser um thisvalor (você não pode escrever Object.getPrototypeOf(super)), apenas uma supermaneira de procurar propriedades, this.__proto__e super.__proto__significa o mesmo, desde que __proto__também não esteja definido em nenhum lugar inferior à cadeia de protótipos.

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-
fonte
Eu já suspeitava que isso tivesse algo a ver com __proto__realmente ser funções acessoras Object.prototypee operar com o thisvalor delas . Mas eu simplesmente não poderia imaginar que isso superfosse especificado para funcionar dessa maneira. Eu pensei em superser aproximadamente equivalente a this.__proto__.__proto__, então super.__proto__teria sido equivalente ao this.__proto__.__proto__.__proto__que teria exibido o comportamento que eu estava esperando. Você sabe onde, nas especificações, o comportamento exato de superé especificado?
Jens Moser
@JensMoser: Vou encontrá-lo daqui a pouco, mas imagine usos normais de super, como super.setFoo('bar'). Você não gostaria que isso funcionasse em um protótipo em vez da instância.
Ry-
@georg Eu sei que __proto__é uma propriedade acessora Object.prototype. Quando pedi uma referência às especificações, quis dizer uma referência ao comportamento exato da superpalavra - chave em conjunto com __proto__. Veja meu comentário anterior.
Jens Moser
@ Ry- Sim, eu simplifiquei um pouco. Meu entendimento exato super.setFoo('bar')seria que isso é equivalente a this.__proto__.__proto__.setFoo.call(this, 'bar'). Portanto, superinvoca automaticamente as funções com o correto this.
Jens Moser
11
@JensMoser super.__proto__(em que o método da Level3classe) é exatamente equivalente aReflect.get(Object.getPrototypeOf(Level3.prototype), "__proto__", this)
Bergi