Dlaczego w JavaScript jest (super .__ proto__ === to .__ proto__) prawda?

10

Wygląda na to, że w klasach JavaScript (ES6) super.__proto__ === this.__proto__.

Czy możesz wyjaśnić, dlaczego tak jest? Zachowanie wydaje się spójne w różnych przeglądarkach, więc podejrzewam, że jest to określone gdzieś w specyfikacji.

Rozważ następujący kod:

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

Spodziewałbym się, super.__proto__.myFunc();że nazwałoby to funkcję myFunc()klasy Level1i to super.__proto__ !== this.__proto__. Zamiast tego super.__proto__.myFunc();faktycznie wywołuje myFunc()klasę Level3(sama się nazywa), a następnie przy drugim wywołaniu wywołuje myFunc()klasę Level2. Jest to całkowicie zrozumiałe, jeśli super.__proto__ === this.__proto__pokazuje to kod.

Czy możesz wyjaśnić powód, dla którego super.__proto__ === this.__proto__w tym przykładzie? Jeśli to możliwe, proszę również podać odniesienia do odpowiedniej sekcji specyfikacji.

Jens Moser
źródło

Odpowiedzi:

6

Object.prototype.__proto__ jest własnością z geterem [1] . Działa na podstawie swojej thiswartości. Nie ma rzeczywistego superobiektu, thisktóry mógłby być wartością (nie można było napisać Object.getPrototypeOf(super)), jest to tylko supersposób wyszukiwania właściwości, this.__proto__a więc super.__proto__oznacza to samo, o ile __proto__nie jest zdefiniowane nigdzie niżej w łańcuchu prototypów.

Porównać:

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-
źródło
Podejrzewałem już, że ma to coś wspólnego z faktem, że __proto__funkcje akcesora działają Object.prototypei działają na ich thiswartości. Ale po prostu nie mogę sobie wyobrazić, że supertak naprawdę działało w ten sposób. Myślałem, że będę supermniej więcej równorzędny this.__proto__.__proto__, więc super.__proto__byłby równoważny z this.__proto__.__proto__.__proto__tym, co pokazałoby zachowanie, którego się spodziewałem. Czy wiesz, gdzie w specyfikacji superpodano dokładne zachowanie ?
Jens Moser
@JensMoser: Zajmę się tym trochę, ale wyobraź sobie normalne zastosowania super, takie jak super.setFoo('bar'). Nie chciałbyś, aby działał na prototypie zamiast instancji.
Ry-
@georg Wiem, że __proto__jest to właściwość akcesorium Object.prototype. Kiedy poprosiłem o odniesienie do specyfikacji, miałem na myśli odniesienie do dokładnego zachowania supersłowa kluczowego w połączeniu z __proto__. Zobacz mój poprzedni komentarz.
Jens Moser
@ Ry- Tak, trochę uprościłem. Dokładnie rozumiem super.setFoo('bar'), że jest to równoważne this.__proto__.__proto__.setFoo.call(this, 'bar'). Tak więc superautomatycznie wywołuje funkcje z poprawnym this.
Jens Moser
1
@JensMoser super.__proto__(w tej metodzie Level3klasy) jest dokładnie równoważnyReflect.get(Object.getPrototypeOf(Level3.prototype), "__proto__", this)
Bergi,