Почему в JavaScript (супер .__ proto__ === это .__ proto__) верно?

10

Похоже, в JavaScript (ES6) классы super.__proto__ === this.__proto__.

Можете ли вы объяснить, почему это так? Поведение кажется одинаковым для разных браузеров, поэтому я подозреваю, что это указано где-то в спецификации.

Рассмотрим следующий код:

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

Я ожидал, super.__proto__.myFunc();что вызовет функцию myFunc()класса Level1и тому подобное super.__proto__ !== this.__proto__. Вместо этого super.__proto__.myFunc();фактически вызывает myFunc()класс Level3(он сам себя вызывает), а затем при втором вызове он вызывает myFunc()класс Level2. Это вполне понятно, если super.__proto__ === this.__proto__какой код демонстрирует.

Можете ли вы объяснить причину, почему super.__proto__ === this.__proto__в этом примере? Если возможно, просьба также предоставить ссылки на соответствующий раздел спецификации.

Йенс Мозер
источник

Ответы:

6

Object.prototype.__proto__это свойство с геттером [1] . Он действует на свою thisценность. Нет фактического superобъекта, который мог бы быть thisзначением (вы не могли бы написать Object.getPrototypeOf(super)), просто superспособ поиска свойств, this.__proto__и это super.__proto__означало бы то же самое, если __proto__оно не определено нигде ниже в цепочке прототипов.

Для сравнения:

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-
источник
Я уже подозревал, что это как-то связано с тем, чтобы на __proto__самом деле использовать функции доступа Object.prototypeи работать на их thisзначение. Но я просто не могу себе представить, что на superсамом деле было указано, чтобы работать таким образом. Я думал о superтом this.__proto__.__proto__, чтобы быть примерно эквивалентным , поэтому super.__proto__был бы эквивалентен тому, this.__proto__.__proto__.__proto__что показало бы поведение, которое я ожидал. Вы знаете, где в спецификации указано точное поведение super?
Йенс Мозер
@JensMoser: Я найду это немного, но представьте себе нормальное использование super, вроде super.setFoo('bar'). Вы бы не хотели, чтобы это работало на прототипе, а не на экземпляре.
Ry-
@georg Я знаю, что __proto__это свойство аксессора Object.prototype. Когда я попросил ссылку на спецификацию, я имел в виду ссылку на точное поведение superключевого слова в сочетании с __proto__. Смотрите мой предыдущий комментарий.
Йенс Мозер
@ Ry- Да, я немного упростила. Мое точное понимание super.setFoo('bar')было бы, что это эквивалентно this.__proto__.__proto__.setFoo.call(this, 'bar'). Итак, superавтоматически вызывает функции с правильными this.
Дженс Мозер
1
@JensMoser super.__proto__(в этом способе Level3класса) в точности эквивалентноReflect.get(Object.getPrototypeOf(Level3.prototype), "__proto__", this)
Берги