Apa perbedaan __proto__ dari constructor.prototype?

163
function Gadget(name, color)
{
   this.name = name;
   this.color = color;
}

Gadget.prototype.rating = 3

var newtoy = new Gadget("webcam", "black")

newtoy.constructor.prototype.constructor.prototype.constructor.prototype 

Itu selalu mengembalikan objek dengan rating = 3.

Tetapi jika saya melakukan hal berikut:

newtoy.__proto__.__proto__.__proto__

Rantai itu akhirnya kembali null.

Juga di Internet Explorer bagaimana saya akan memeriksa null jika tidak ada __proto__properti?

xdevel2000
sumber
30
Diagram grafik ini akan membantu Anda memahami perbedaan antara prototipe dan proto . Anda dapat mengikuti rantai proto dari objek newtoy, dan kemudian Anda akan menyadari mengapa Proto ke-3 dari newtoy adalah null.
bit
Juga jelas dari diagram yang newtoy.prototypetidak sama dengan newtoy.constructor.prototypedan karenanya newtoy.constructor.prototypetidak akan memiliki properti yang dipanggil rating. Demikian pula, newtoy.constructor.prototype.constructor.propertyproperti tidak akan dipanggil rating.
bit
Mengetik di komentar terakhir: karena itu newtoy.constructor.prototypeakan memiliki properti yang disebut peringkat. Demikian pula newtoy.constructor.prototype.constructor.propertyakan memiliki peringkat properti yang disebut.
bit
1
@Royi Namir Saya telah mengunggah jsViz di github. Ini adalah situs demo . Tolong jangan pedulikan seberapa tidak terurus (dan kotor) kode yang sebenarnya. Proyek super lama yang belum pernah saya sentuh selamanya.
bit

Jawaban:

210

Saya sudah mencoba untuk membungkus kepala saya di sekitar ini baru-baru ini dan akhirnya muncul dengan "peta" ini yang saya pikir menjelaskan sepenuhnya tentang masalah ini

http://i.stack.imgur.com/KFzI3.png masukkan deskripsi gambar di sini

Saya tahu saya bukan yang pertama mengada-ada tetapi lebih menarik untuk mengetahuinya dengan menemukannya :-). Lagi pula, setelah itu saya menemukan misalnya ini diagram lain yang saya pikir mengatakan pada dasarnya sama:

Tata letak objek Javascript

Hal yang paling mengejutkan bagi saya adalah menemukan Object.__proto__hal itu Function.prototype, alih-alih Object.prototype, tapi saya yakin ada alasan bagus untuk itu :-)

Saya menempelkan kode yang disebutkan dalam gambar di sini juga karena jika ada yang ingin mengujinya. Perhatikan bahwa beberapa properti ditambahkan ke objek untuk memudahkan mengetahui di mana kita setelah beberapa lompatan:

Object.O1='';
Object.prototype.Op1='';

Function.F1 = '';
Function.prototype.Fp1 = '';

Cat = function(){};
Cat.C1 = '';
Cat.prototype.Cp1 = '';

mycat = new Cat();
o = {};

// EDITED: using console.dir now instead of console.log
console.dir(mycat);
console.dir(o);
drodsou
sumber
2
@utsaina Sangat keren. Periksa representasi grafis lain dari kode yang diposting OP. Dan saya pikir diagram kami sepakat dalam hal rincian teknis.
bit
43
Alasan mengapa Object.__proto__menunjuk Function.prototypeadalah karena Object()dengan sendirinya adalah fungsi asli yang instantiates objek kosong. Karena itu, Object()adalah suatu fungsi. Anda akan menemukan bahwa semua __proto__properti tipe asli utama lainnya mengarah ke Function.prototype. Object, Function, String, Number, Dan Arraysemua mewarisi Fungsi prototipe.
Putar
@rodsou tautan ke-2 Anda mengagumkan. Tolong periksa sekarang;) mollypages.org/misc/js.mp Penjelasan bagus: D
abhisekp
@Sivel "Oleh karena itu, Object () adalah fungsi" - maksud Anda mengatakan Object adalah fungsi? tanpa ()
giorgim
2
@GiorgiMoniava Benar. Objectitu sendiri adalah fungsi; hasil dari mengeksekusi callable Object(yaitu, nilai balik dari menjalankan Object()) bukan suatu fungsi.
Putar
67

constructoradalah properti [[DontEnum]] yang telah ditentukan sebelumnya dari objek yang ditunjuk oleh prototypeproperti dari objek fungsi dan pada awalnya akan menunjuk ke objek fungsi itu sendiri.

__proto__ setara dengan properti [[Prototipe]] internal suatu objek, yaitu prototipe aslinya.

Saat Anda membuat objek dengan newoperator, properti [[Prototipe]] internalnya akan ditetapkan ke objek yang ditunjuk oleh prototypeproperti fungsi konstruktor .

Ini berarti bahwa .constructorakan mengevaluasi .__proto__.constructor, yaitu fungsi konstruktor yang digunakan untuk membuat objek, dan seperti yang telah kita pelajari, protoypeproperti fungsi ini digunakan untuk mengatur objek [[Prototipe]] objek.

Itu mengikuti yang .constructor.prototype.constructoridentik dengan .constructor(selama properti ini belum ditimpa); lihat di sini untuk penjelasan lebih rinci.

Jika __proto__ tersedia, Anda dapat berjalan di rantai prototipe objek yang sebenarnya. Tidak ada cara untuk melakukan ini di ECMAScript3 karena JavaScript tidak dirancang untuk hierarki warisan yang mendalam.

Christoph
sumber
3
Tautan 'di sini' adalah standar emas. Pergi ke sana jika Anda ingin deskripsi lengkap.
Ricalsin
Tangkapan yang bagus dengan .constructor.prototyperantai. Saya juga tidak jelas untuk saya, sementara saya tidak melihat itu .constructorsetara .__proto__.constructor. Yang berarti bersepeda antara fungsi konstruktor dan prototipe itu.
Johnny_D
30

Warisan Prototypal dalam JavaScript didasarkan pada __proto__properti dalam arti bahwa setiap objek mewarisi konten objek yang direferensikan oleh__proto__ propertinya.

The prototypeproperti khusus hanya untuk Functionobjek dan hanya ketika menggunakan newoperator untuk memanggil Functionsebagai konstruktor. Dalam hal ini, objek yang dibuat __proto__akan diatur ke konstruktor Function.prototype.

Ini berarti bahwa menambahkan ke Function.prototypeakan secara otomatis mencerminkan semua objek yang __proto__mereferensikanFunction.prototype .

Mengganti konstruktor Function.prototypedengan objek lain tidak akan diperbarui__proto__ properti untuk objek yang sudah ada.

Perhatikan bahwa __proto__properti tidak boleh diakses secara langsung, Object.getPrototypeOf (objek) harus digunakan sebagai gantinya.

Untuk menjawab pertanyaan pertama, saya telah membuat diagram __proto__dan prototypereferensi yang dipesan lebih dahulu , sayangnya stackoverflow tidak memungkinkan saya untuk menambahkan gambar dengan "kurang dari 10 reputasi". Mungkin lain kali.

[Sunting] Angka tersebut digunakan [[Prototype]]sebagai ganti __proto__karena itu adalah bagaimana spesifikasi ECMAScript merujuk ke objek internal. Saya harap Anda bisa memikirkan semuanya.

Berikut adalah beberapa petunjuk untuk membantu Anda memahami angka tersebut:

red    = JavaScript Function constructor and its prototype
violet = JavaScript Object constructor and its prototype
green  = user-created objects
         (first created using Object constructor or object literal {},
          second using user-defined constructor function)
blue   = user-defined function and its prototype
         (when you create a function, two objects are created in memory:
          the function and its prototype)

Perhatikan bahwa constructorproperti tidak ada di objek yang dibuat, tetapi diwarisi dari prototipe.

masukkan deskripsi gambar di sini

xorcus
sumber
@xorcus Bisakah Anda jelaskan ini: new MyFunction()membuat instance objek yang __proto__seharusnya merujuk pada prototipe ctor-nya yang MyFunction.prototype.Jadi mengapa MyFunction.prototype.__proto__merujuk Object.prototype? itu harus merujuk (seperti sampel pertama saya) ke prototipe MyFunction.prototypeMyFunction.prototypeMyfunction
aktornya
@Royi Namir: MyFunction.prototype .__ proto__ merujuk ke Object.prototype karena MyFunction.prototype adalah objek. Object.prototype diwarisi oleh semua objek (biasanya, di situlah rantai prototipe warisan berakhir). Saya tidak setuju bahwa MyFunction.prototype adalah turunan dari MyFunction. obj instanceof MyFunction <=> MyFunction.prototype.isPrototypeOf (obj) <=> MyFunction.prototype ada di rantai prototipe objek. Itu bukan kasus untuk objek MyFunction.prototype
xorcus
14

Objectadalah Hawa, dan FunctionAdam, Adam ( Function) menggunakan tulangnya ( Function.prototype) untuk membuat Hawa ( Object). Lalu siapa yang menciptakan Adam (Function )? - Penemu bahasa JavaScript :-).

Menurut jawaban utsaina, saya ingin menambahkan info yang lebih berguna.

Hal yang paling mengejutkan bagi saya adalah menemukan Object.__proto__ hal itu Function.prototype, alih-alih Object.prototype, tapi saya yakin ada alasan bagus untuk itu :-)

Seharusnya TIDAK. Object.__proto__seharusnya TIDAK menunjuk ke Object.prototype. Sebaliknya, contoh dari Object o, o.__proto__harus menunjuk keObject.prototype .

(Maafkan saya karena menggunakan ketentuan classdaninstance dalam JavaScript, tetapi Anda tahu itu :-)

Saya pikir kelas Objectitu sendiri adalah contoh dari Function, itu sebabnya Object.__proto__ === Function.prototype. Karena itu: Objectadalah Hawa, dan FunctionAdam, Adam ( Function) menggunakan tulangnya ( Function.prototype) untuk membuat Hawa (Object ).

Lebih jauh, bahkan kelas Functionitu sendiri adalah turunan dari Functiondirinya sendiri, yaituFunction.__proto__ === Function.prototype sebabnyaFunction === Function.constructor

Lebih jauh lagi, kelas reguler Catadalah turunan dari Function, yaitu Cat.__proto__ === Function.prototype.

Alasan di atas adalah, ketika kita membuat kelas dalam JavaScript, sebenarnya, kita hanya membuat fungsi, yang seharusnya menjadi instance dari Function. Objectdan Functionhanya istimewa, tetapi mereka masih kelas, sedangkan Catkelas reguler.

Faktanya, di mesin Google Chrome JavaScript, berikut 4:

  • Function.prototype
  • Function.__proto__
  • Object.__proto__
  • Cat.__proto__

Mereka semua ===(sama dengan) 3 lainnya, dan nilainyafunction Empty() {}

> Function.prototype
  function Empty() {}
> Function.__proto__
  function Empty() {}
> Object.__proto__
  function Empty() {}
> Cat.__proto__
  function Empty() {}
> Function.prototype === Function.__proto__
  true
> Function.__proto__ === Object.__proto__
  true
> Object.__proto__ === Cat.__proto__
  true

BAIK. Lalu siapa yang menciptakan special function Empty() {}( Function.prototype)? Pikirkan tentang itu :-)

Peter Lee
sumber
Setuju dengan itu, kecuali untuk satu hal terakhir: apa yang function Empty() {}Anda maksud dengan menyamakan dengan Function.prototype, dll?, Kode apa yang Anda gunakan di konsol chrome?
drodsou
2
Saya mengoreksi satu hal terakhir yang Anda tunjukkan. Nilainya ada function Empty() {}di Google Chrome. Saya juga menambahkan output konsol.
Peter Lee
semua fungsi adalah instance dari Function, dan oleh karena itu, semua fungsi mewarisi ( _ _proto_ _) dari Function.protot. Sesederhana itu :)
xorcus
Maaf telah mengomentari utas lama. Tetapi apakah mereka diciptakan oleh Penemu Bahasa?
Patel Parth
6

Saya benar-benar tidak tahu mengapa orang tidak mengoreksi Anda tentang di mana sebenarnya masalah dalam pemahaman Anda.

Ini akan membuat Anda lebih mudah menemukan masalah

Jadi mari kita lihat apa yang terjadi:

var newtoy = new Gadget("webcam", "black")

newtoy 
  .constructor //newtoy's constructor function is newtoy ( the function itself)
    .prototype // the function has a prototype property.( all functions has)
      .constructor // constructor here is a **property** (why ? becuase you just did `prototype.constructor`... see the dot ? )  ! it is not(!) the constructor function  !!! this is where your mess begins. it points back to the constructor function itself ( newtoy function)
         .prototype // so again we are at line 3 of this code snippet
            .constructor //same as line 4 ...
                .prototype 
                 rating = 3

Bagus, jadi sekarang mari kita lihat ini __proto__

Sebelum itu, harap diingat 2 hal mengenai __proto__ :

  1. Ketika Anda membuat objek dengan newoperator, internal [[Prototype]]/ proto__propertinya akan disetel ke prototypeproperti (1) constructor functionatau "penciptanya" jika Anda mau.

  2. Kode keras dalam JS -: Object.prototype.__proto__is null.

Mari kita lihat 2 poin ini sebagai " bill"

newtoy
     .__proto__ // When `newtoy` was created , Js put __proto__'s value equal to the value of the cunstructor's prototype value. which is `Gadget.prototype`.
       .__proto__ // Ok so now our starting point is `Gadget.prototype`. so  regarding "bill" who is the constructor function now? watch out !! it's a simple object ! a regular object ! prototype is a regular object!! so who is the constructor function of that object ? Right , it's the `function Object(){...}`.  Ok .( continuing "bill" ) does it has a `prototype` property ? sure. all function has. it's `Object.prototype`. just remember that when Gadget.prototype was created , it's internal `__proto__` was refered to `Object.prototype` becuase as "bill" says :"..will be set to the `prototype` property of   its `constructor function`"
          .__proto__ // Ok so now our satrting point is `Object.prototype`. STOP. read bullet 2.Object.prototype.__proto__ is null by definition. when Object.prototype ( as an object) was created , they SET THE __PROTO__ AS NULL HARDCODED

Lebih baik?

Royi Namir
sumber
2

Setiap fungsi menciptakan prototipe itu. Dan ketika kita membuat objek menggunakan konstruktor fungsi itu, maka properti __proto__ dari objek saya akan mulai menunjuk ke prototipe fungsi itu.

Apoorv Nag
sumber
1
Saya pikir Anda bermaksud mengatakan __proto__properti itu.
demisx
Iya. Aku berarti proto properti dari objek. Saya harap informasinya bermanfaat.
Apoorv Nag
2

Jika semua angka itu luar biasa, mari kita lihat apa artinya properti.

STH.prototype

Saat membuat fungsi baru, ada objek kosong yang dibuat secara paralel dan ditautkan ke fungsi dengan [[Prototype]]rantai. Untuk mengakses objek ini, kami menggunakan prototypeproperti fungsi.

function Gadget() {}
// in background, new object has been created
// we can access it with Gadget.prototype
// it looks somewhat like {constructor: Gadget}

Ingatlah bahwa prototypeproperti hanya tersedia untuk fungsi.

STH.constructor

Objek prototipe yang disebutkan di atas tidak memiliki properti kecuali satu - constructor. Properti ini mewakili fungsi yang membuat objek prototipe.

var toy = new Gadget();

Saat membuat Gadgetfungsi, kami juga membuat objek {constructor: Gadget}- tidak seperti itu Gadget.prototype. Sebagai constructormengacu pada fungsi yang menciptakan sebuah prototipe objek, toy.constructormerupakan Gadgetfungsi. Kami menulis toy.constructor.prototypedan menerima{constructor: Gadget} lagi.

Oleh karena itu, ada lingkaran setan: Anda dapat menggunakan toy.constructor.prototype.constructor.prototype.constructor.prototype.constructor.prototype.constructor.prototype.constructor.prototype.constructor.prototype.constructor.prototypedan itu akan selalu terjadi Gadget.prototype.

toy
.constructor    // Gadget
.prototype    // {constructor: Gadget}
.constructor    // Gadget
.prototype    // {constructor: Gadget}
// ...

STH .__ proto__

Sementara prototypeadalah properti khusus untuk fungsi, __proto__tersedia untuk semua objek saat diletakkan Object.prototype. Ini mengacu pada prototipe fungsi yang dapat membuat objek.

[].__proto__ === Array.prototype
// true

({}).__proto === Object.prototype
// true

Ini toy.__proto__dia Gadget.prototype. Seperti halnya Gadget.prototypeobjek ( {}) dan objek dibuat dengan Objectfungsi (lihat contoh di atas), kita dapatkan Object.prototype. Ini adalah objek yang lebih tinggi dalam JavaScript dan __proto__hanya dapat menunjukkan null.

toy
.__proto__    // Gadget.prototype (object looking like {constructor: Gadget})
.__proto__    // Object.prototype (topmost object in JS)
.__proto__    // null - Object.prototype is the end of any chain
Damian Czapiewski
sumber
0

Jawaban singkat: __proto__adalah referensi ke prototypeproperti konstruktor yang menciptakan objek.

Objek dalam JavaScript

Objek JavaScript adalah tipe bawaan untuk koleksi sifat nol atau lebih. Properti adalah wadah yang menampung benda lain, nilai primitif, atau fungsi.

Konstruktor dalam JavaScript

Fungsi adalah objek biasa (yang menerapkan [[Call]]dalam istilah ECMA-262) dengan kemampuan tambahan untuk dipanggil tetapi memainkan peran lain dalam JavaScript: mereka menjadi konstruktor ( pabrik untuk objek) jika dipanggil melaluinew operator. Konstruktor merupakan analog kasar ke kelas dalam bahasa lain.

Setiap fungsi JavaScript sebenarnya adalah instance dari Functionobjek fungsi bawaan yang memiliki properti khusus bernama yang prototypedigunakan untuk mengimplementasikan warisan berbasis prototipe dan properti bersama. Setiap objek yang dibuat oleh fungsi konstruktor memiliki referensi implisit (disebut prototipe atau __proto__) dengan nilai konstruktornya prototype.

Konstruktor prototypeadalah semacam cetak biru untuk membangun objek karena setiap objek yang dibuat oleh konstruktor mewarisi referensi untuk ituprototype .

Rantai prototipe

Objek menentukan prototipe melalui properti internal [[Prototype]]atau __proto__. Hubungan prototipe antara dua objek adalah tentang warisan: setiap objek dapat memiliki objek lain sebagai prototipe. Prototipe mungkin nullnilainya.

Rantai benda yang dihubungkan oleh __proto__properti disebut rantai prototipe . Ketika referensi dibuat untuk properti di objek, referensi itu adalah properti yang ditemukan di objek pertama dalam rantai prototipe yang berisi properti dari nama itu. Rantai prototipe berperilaku seolah-olah itu adalah objek tunggal.

Lihat gambar ini (diekstrak dari blog ini ):

proto.jpg

Setiap kali Anda mencoba mengakses properti di suatu objek, JavaScript memulai pencariannya di objek itu dan melanjutkan dengan prototipe, prototipe prototipe, dan seterusnya hingga properti ditemukan atau jika __proto__memiliki nilai null.

Jenis warisan yang menggunakan rantai prototipe ini sering disebut delegasi untuk menghindari kebingungan dengan bahasa lain menggunakan rantai kelas.

Hampir semua objek adalah instance dari Object, karena Object.prototypeterakhir dalam rantai prototipe mereka Tetapi Object.prototypeini bukan contoh Objectkarena Object.prototype.__proto__memegang nilai null.

Anda juga dapat membuat objek dengan nullprototipe seperti ini:

var dict = Object.create(null);

Benda seperti adalah peta yang lebih baik (kamus) dari objek literal, yang mengapa pola ini kadang-kadang disebut dict pola ( dict kamus).

Catatan: literal objek yang dibuat menggunakan {}adalah contoh Objectkarena ({}).__proto__merupakan acuan untuk Object.prototype.

eigenslacker
sumber
Silakan kutip sumber kutipan dan artefak yang Anda gunakan. Gambar tampaknya berasal dari giamir.com/pseudoclasses-and-prototypal-inheritance-in-JS , apakah Anda memiliki hak cipta di atasnya?
Bergi
@Bergi saya mengutip sumber gambar. Sebagian besar kutipan yang saya gunakan diekstraksi dari standar JS atau MDN
eigenslacker