Mengapa dalam JavaScript (super .__ proto__ === this .__ proto__) benar?

10

Tampaknya dalam Kelas JavaScript (ES6) super.__proto__ === this.__proto__.

Bisakah Anda jelaskan mengapa ini masalahnya? Perilaku ini tampaknya konsisten di berbagai browser, jadi saya curiga ini ditentukan di suatu tempat dalam spesifikasi.

Pertimbangkan kode berikut:

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

Saya akan berharap itu super.__proto__.myFunc();akan memanggil fungsi myFunc()kelas Level1dan itu super.__proto__ !== this.__proto__. Sebaliknya super.__proto__.myFunc();sebenarnya panggilan myFunc()kelas Level3(panggilan itu sendiri) dan kemudian pada doa kedua panggilan myFunc()kelas Level2. Ini sangat bisa dipahami jika super.__proto__ === this.__proto__kode menunjukkan.

Bisakah Anda menjelaskan alasannya mengapa super.__proto__ === this.__proto__dalam contoh ini? Jika memungkinkan, berikan juga referensi ke bagian spesifikasi yang relevan.

Jens Moser
sumber

Jawaban:

6

Object.prototype.__proto__adalah properti dengan pengambil [1] . Ini beroperasi pada thisnilainya. Tidak ada superobjek aktual untuk menjadi thisnilai (Anda tidak bisa menulis Object.getPrototypeOf(super)), hanya supercara mencari properti, jadi this.__proto__dan super.__proto__berarti hal yang sama selama __proto__tidak juga didefinisikan di tempat yang lebih rendah pada rantai prototipe.

Membandingkan:

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-
sumber
Saya sudah menduga bahwa ini ada hubungannya dengan __proto__benar-benar menjadi fungsi pengakses Object.prototypedan beroperasi pada thisnilai mereka . Tapi saya tidak bisa membayangkan superitu sebenarnya ditentukan untuk bekerja dengan cara ini. Saya pikir superkira-kira setara dengan this.__proto__.__proto__, jadi super.__proto__akan sama dengan this.__proto__.__proto__.__proto__yang akan menunjukkan perilaku yang saya harapkan. Apakah Anda tahu, di mana dalam spesifikasi perilaku yang tepat superditentukan?
Jens Moser
@JensMoser: Saya akan menemukannya sedikit, tetapi bayangkan penggunaan normal super, seperti super.setFoo('bar'). Anda tidak ingin itu beroperasi pada prototipe alih-alih instance.
Ry-
@georg Saya tahu __proto__ini adalah properti accessor Object.prototype. Ketika saya meminta referensi ke spec, saya berarti referensi untuk perilaku yang tepat dari superkata kunci dalam hubungannya dengan __proto__. Lihat komentar saya sebelumnya.
Jens Moser
@ Ry- Ya, saya sedikit menyederhanakan. Pemahaman saya yang tepat super.setFoo('bar')akan, bahwa itu setara dengan this.__proto__.__proto__.setFoo.call(this, 'bar'). Jadi, supersecara otomatis memanggil fungsi dengan benar this.
Jens Moser
1
@JensMoser super.__proto__(dalam metode Level3kelas) persis sama denganReflect.get(Object.getPrototypeOf(Level3.prototype), "__proto__", this)
Bergi