Bagaimana cara kerja JavaScript .prototype?


2041

Saya tidak terlalu suka bahasa pemrograman yang dinamis tapi saya sudah menulis kode JavaScript yang adil. Saya tidak pernah benar-benar memusatkan perhatian pada pemrograman berbasis prototipe ini, apakah ada yang tahu cara kerjanya?

var obj = new Object();
obj.prototype.test = function() { alert('Hello?'); };
var obj2 = new obj();
obj2.test();

Saya ingat banyak diskusi yang saya lakukan dengan orang-orang beberapa waktu lalu (saya tidak begitu yakin apa yang saya lakukan) tetapi ketika saya memahaminya, tidak ada konsep kelas. Itu hanya sebuah objek, dan contoh dari objek tersebut adalah klon dari yang asli, bukan?

Tapi apa tujuan sebenarnya dari properti ".prototype" ini di JavaScript? Bagaimana hubungannya dengan objek instantiating?

Perbarui: cara yang benar

var obj = new Object(); // not a functional object
obj.prototype.test = function() { alert('Hello?'); }; // this is wrong!

function MyObject() {} // a first class functional object
MyObject.prototype.test = function() { alert('OK'); } // OK

Slide ini juga sangat membantu.


78
John Resig memiliki beberapa slide tentang prototipe fungsi yang membantu saya ketika melihat ke subjek (Anda juga dapat membuat perubahan pada kode dan melihat apa yang terjadi ...) http://ejohn.org/apps/learn/#64
John Foster

5
Bahan referensi yang bagus, untuk keperluan menjaga agar pertanyaan ini tetap informatif, mungkin tempatkan beberapa komentar dari situs John pada jawaban Anda seandainya situsnya berubah sehingga tautan Anda tidak lagi tersedia. Either way +1, membantu saya.
Chris

95
+1 untuk tautan Anda ke slide JavaScript Ninja John Resig # 64 . Mulai dari sana benar-benar bermanfaat, dan saya merasa seperti saya memahami prototipe dengan benar.
kutu buku yang dibayar

4
Apakah kita benar-benar membutuhkan objek fungsional untuk menerapkan prototipe? jika ya daripada mengapa?
Anshul

6
Ini mungkin membantu Anda: webdeveasy.com/javascript-prototype
Naor

Jawaban:


1007

Setiap objek JavaScript memiliki "slot" internal[[Prototype]] yang disebut yang nilainya baik nullatau object. Anda dapat menganggap slot sebagai properti pada objek, internal mesin JavaScript, disembunyikan dari kode yang Anda tulis. Tanda kurung di sekitar [[Prototype]]disengaja, dan merupakan konvensi spesifikasi Script ECMAS untuk menunjukkan slot internal.

Nilai yang ditunjukkan oleh [[Prototype]]objek, secara bahasa dikenal sebagai "prototipe objek itu."

Jika Anda mengakses properti melalui notasi titik ( obj.propName) atau braket ( obj['propName']), dan objek tidak secara langsung memiliki properti seperti itu (mis. Properti sendiri , dapat diperiksa melalui obj.hasOwnProperty('propName')), runtime mencari properti dengan nama pada objek yang direferensikan oleh [[Prototype]]sebaliknya. Jika [[Prototype]] juga tidak memiliki properti seperti itu, maka [[Prototype]]diperiksa pada gilirannya, dan seterusnya. Dengan cara ini, rantai prototipe objek asli berjalan sampai kecocokan ditemukan, atau akhirnya tercapai. Di bagian atas rantai prototipe adalah nullnilainya.

Implementasi JavaScript modern memungkinkan akses baca dan / atau tulis ke [[Prototype]]dalam cara-cara berikut:

  1. The newoperator (mengkonfigurasi rantai prototipe pada objek bawaan kembali dari fungsi konstruktor),
  2. Kata extendskunci (mengkonfigurasi rantai prototipe saat menggunakan sintaks kelas),
  3. Object.createakan menetapkan argumen yang disediakan sebagai [[Prototype]]objek yang dihasilkan,
  4. Object.getPrototypeOfdan Object.setPrototypeOf(dapatkan / atur pembuatan objek [[Prototype]] setelah ), dan
  5. Properti accessor standar (mis. Pengambil / penyetel) bernama __proto__(mirip dengan 4.)

Object.getPrototypeOfdan Object.setPrototypeOflebih disukai __proto__, sebagian karena perilaku o.__proto__ yang tidak biasa ketika sebuah objek memiliki prototipe null.

Suatu objek pada [[Prototype]]awalnya diatur selama pembuatan objek.

Jika Anda membuat objek baru melalui new Func(), objek [[Prototype]]akan, secara default, diatur ke objek yang dirujuk oleh Func.prototype.

Perhatikan bahwa, oleh karena itu, semua kelas, dan semua fungsi yang dapat digunakan dengan newoperator, memiliki properti yang dinamai .prototypedi samping [[Prototype]]slot internal mereka sendiri . Penggunaan ganda kata "prototipe" ini adalah sumber kebingungan yang tak berkesudahan di antara para pendatang baru dalam bahasa tersebut.

Menggunakan newdengan fungsi konstruktor memungkinkan kita untuk mensimulasikan pewarisan klasik dalam JavaScript; meskipun sistem pewarisan JavaScript adalah - seperti yang telah kita lihat - prototipe, dan bukan berbasis kelas.

Sebelum pengenalan sintaks kelas ke JavaScript, fungsi konstruktor adalah satu-satunya cara untuk mensimulasikan kelas. Kita dapat memikirkan properti objek yang dirujuk oleh .prototypeproperti fungsi konstruktor sebagai anggota bersama; yaitu. anggota yang sama untuk setiap instance. Dalam sistem berbasis kelas, metode diimplementasikan dengan cara yang sama untuk setiap instance, sehingga metode secara konseptual ditambahkan ke .prototypeproperti; bidang objek, bagaimanapun, adalah khusus-contoh dan karena itu ditambahkan ke objek itu sendiri selama konstruksi.

Tanpa sintaksis kelas, pengembang harus secara manual mengkonfigurasi rantai prototipe untuk mencapai fungsi yang mirip dengan warisan klasik. Ini mengarah pada banyak cara berbeda untuk mencapai ini.

Ini salah satu caranya:

function Child() {}
function Parent() {}
Parent.prototype.inheritedMethod = function () { return 'this is inherited' }

function inherit(child, parent) {
  child.prototype = Object.create(parent.prototype)
  child.prototype.constructor = child
  return child;
}

Child = inherit(Child, Parent)
const o = new Child
console.log(o.inheritedMethod()) // 'this is inherited'

... dan inilah cara lain:

function Child() {}
function Parent() {}
Parent.prototype.inheritedMethod = function () { return 'this is inherited' }

function inherit(child, parent) {
    function tmp() {}
    tmp.prototype = parent.prototype
    const proto = new tmp()
    proto.constructor = child
    child.prototype = proto
    return child
}

Child = inherit(Child, Parent)
const o = new Child
console.log(o.inheritedMethod()) // 'this is inherited'

Sintaks kelas yang diperkenalkan dalam ES2015 menyederhanakan banyak hal, dengan menyediakan extendssebagai "satu cara yang benar" untuk mengkonfigurasi rantai prototipe untuk mensimulasikan pewarisan klasik dalam JavaScript.

Jadi, mirip dengan kode di atas, jika Anda menggunakan sintaks kelas untuk membuat objek baru seperti:

class Parent { inheritedMethod() { return 'this is inherited' } }
class Child extends Parent {}

const o = new Child
console.log(o.inheritedMethod()) // 'this is inherited'

... objek yang dihasilkan [[Prototype]]akan diatur ke instance Parent, yang [[Prototype]], pada gilirannya, adalah Parent.prototype.

Akhirnya, jika Anda membuat objek baru melalui Object.create(foo), objek yang dihasilkan [[Prototype]]akan diatur ke foo.


1
Jadi, saya melakukan sesuatu yang salah dengan mendefinisikan properti baru pada properti prototipe di cuplikan singkat saya?
John Leidegren

3
Saya pikir inilah artinya memiliki objek fungsi sebagai warga negara kelas satu.
John Leidegren

8
Saya benci hal-hal yang tidak standar, terutama dalam bahasa pemrograman, mengapa bahkan ada proto ketika itu jelas tidak diperlukan?
John Leidegren

1
@John __proto__ diperlukan hanya untuk penggunaan internal oleh penerjemah JS. Setiap Objek perlu mengetahui properti dan metode apa yang merupakan bagian dari prototipe dan yang merupakan bagian dari Objek itu sendiri. Juru bahasa JS harus dapat memanggil metode prototyped pada Object.
BMiner

17
perhatikan bahwa penggunaan [[Prototipe]] disengaja - ECMA-262 melampirkan nama properti internal dengan tanda kurung ganda
Christoph

1798

Dalam bahasa yang mengimplementasikan warisan klasik seperti Java, C # atau C ++ Anda mulai dengan membuat kelas - cetak biru untuk objek Anda - dan kemudian Anda bisa membuat objek baru dari kelas itu atau Anda bisa memperluas kelas, mendefinisikan kelas baru yang menambah kelas aslinya.

Di JavaScript Anda pertama kali membuat objek (tidak ada konsep kelas), maka Anda dapat menambah objek Anda sendiri atau membuat objek baru dari sana. Ini tidak sulit, tetapi sedikit asing dan sulit dimetabolisme untuk seseorang yang terbiasa dengan cara klasik.

Contoh:

//Define a functional object to hold persons in JavaScript
var Person = function(name) {
  this.name = name;
};

//Add dynamically to the already defined object a new getter
Person.prototype.getName = function() {
  return this.name;
};

//Create a new object of type Person
var john = new Person("John");

//Try the getter
alert(john.getName());

//If now I modify person, also John gets the updates
Person.prototype.sayMyName = function() {
  alert('Hello, my name is ' + this.getName());
};

//Call the new method on john
john.sayMyName();

Sampai sekarang saya telah memperluas objek dasar, sekarang saya membuat objek lain dan kemudian mewarisi dari Person.

//Create a new object of type Customer by defining its constructor. It's not 
//related to Person for now.
var Customer = function(name) {
    this.name = name;
};

//Now I link the objects and to do so, we link the prototype of Customer to 
//a new instance of Person. The prototype is the base that will be used to 
//construct all new instances and also, will modify dynamically all already 
//constructed objects because in JavaScript objects retain a pointer to the 
//prototype
Customer.prototype = new Person();     

//Now I can call the methods of Person on the Customer, let's try, first 
//I need to create a Customer.
var myCustomer = new Customer('Dream Inc.');
myCustomer.sayMyName();

//If I add new methods to Person, they will be added to Customer, but if I
//add new methods to Customer they won't be added to Person. Example:
Customer.prototype.setAmountDue = function(amountDue) {
    this.amountDue = amountDue;
};
Customer.prototype.getAmountDue = function() {
    return this.amountDue;
};

//Let's try:       
myCustomer.setAmountDue(2000);
alert(myCustomer.getAmountDue());

Sementara seperti yang saya katakan, saya tidak bisa memanggil setAmountDue (), getAmountDue () pada Seseorang.

//The following statement generates an error.
john.setAmountDue(1000);

352
Saya pikir jawaban pada stackoverflow tidak hanya menarik untuk poster aslinya, tetapi juga untuk komunitas besar orang lain yang bersembunyi atau berasal dari pencarian. Dan saya telah menjadi salah satu dari mereka dan saya mendapat manfaat dari posting lama. Saya pikir saya dapat berkontribusi pada jawaban lain dengan menambahkan beberapa contoh kode. Tentang pertanyaan Anda: jika Anda meninggalkan yang baru, itu tidak berfungsi. ketika saya memanggil myCustomer.sayMyName () ia mengembalikan "myCustomer.sayMyName bukan fungsi". Cara termudah adalah bereksperimen dengan pembakar dan melihat apa yang terjadi.
stivlo

7
Sejauh yang saya mengerti var Person = fungsi (nama) {...}; mendefinisikan fungsi konstruktor yang mampu membangun Object Person. Jadi belum ada Objek, hanya fungsi konstruktor anonim yang ditugaskan untuk Orang. Ini adalah penjelasan yang sangat bagus: helephant.com/2008/08/how-javascript-objects-work
stivlo

17
PERINGATAN: Jawaban ini mengabaikan fakta bahwa konstruktor kelas induk tidak dipanggil berdasarkan per instance. Satu-satunya alasan itu berhasil adalah karena ia melakukan hal yang sama persis (menetapkan nama) di konstruktor anak dan orang tua. Untuk penjelasan yang lebih mendalam tentang kesalahan umum yang dibuat ketika mencoba warisan dalam JavaScript (dan solusi akhir), silakan lihat: posting stack overflow ini
Aaren Cordova

3
Saya perhatikan bahwa jawaban ini juga tidak menyebutkan bahwa dengan menggunakan "Orang baru ()" sebagai prototipe, Anda sebenarnya menyetel properti contoh "nama" dari "Orang" menjadi properti statis "Pelanggan" (jadi semua Pelanggan instance akan memiliki properti yang sama). Meskipun ini adalah contoh dasar yang baik, JANGAN MELAKUKANNYA. :) Buat fungsi anonim baru untuk bertindak sebagai "jembatan" dengan menetapkan prototipe ke "Person.prototype", lalu buat instance dari itu dan atur "Customer.prototype" ke instance anonim itu.
James Wilkins

10
Tentang Customer.prototype = new Person();garis, MDN menunjukkan contoh menggunakan Customer.prototype = Object.create(Person.prototype), dan menyatakan bahwa 'Kesalahan umum di sini adalah menggunakan "Orang baru ()"' . sumber
Rafael Eyng

186

Ini adalah model objek berbasis prototipe yang sangat sederhana yang akan dianggap sebagai sampel selama penjelasan, tanpa komentar:

function Person(name){
    this.name = name;
}
Person.prototype.getName = function(){
    console.log(this.name);
}
var person = new Person("George");

Ada beberapa poin penting yang harus kita pertimbangkan sebelum melalui konsep prototipe.

1- Cara kerja sebenarnya fungsi JavaScript:

Untuk mengambil langkah pertama kita harus mencari tahu, bagaimana fungsi JavaScript benar-benar bekerja, sebagai fungsi seperti kelas menggunakan thiskata kunci di dalamnya atau hanya sebagai fungsi biasa dengan argumennya, apa yang dilakukannya dan apa yang dikembalikan.

Katakanlah kita ingin membuat Personmodel objek. tetapi dalam langkah ini saya akan mencoba melakukan hal yang persis sama tanpa menggunakan prototypedan newkata kunci .

Jadi pada langkah ini functions, objectsdan thiskata kunci, adalah semua yang kita miliki.

Pertanyaan pertama adalah bagaimana thiskata kunci dapat berguna tanpa menggunakan newkata kunci .

Jadi untuk menjawabnya katakanlah kita memiliki objek kosong, dan dua fungsi seperti:

var person = {};
function Person(name){  this.name = name;  }

function getName(){
    console.log(this.name);
}

dan sekarang tanpa menggunakan newkata kunci bagaimana kita bisa menggunakan fungsi-fungsi ini. Jadi JavaScript memiliki 3 cara berbeda untuk melakukannya:

Sebuah. Cara pertama adalah memanggil fungsi sebagai fungsi biasa:

Person("George");
getName();//would print the "George" in the console

dalam hal ini, ini akan menjadi objek konteks saat ini, yang biasanya merupakan windowobjek global di browser atau GLOBALdi Node.js. Itu berarti kita akan memiliki, window.name di browser atau GLOBAL.name di Node.js, dengan "George" sebagai nilainya.

b. Kita bisa melampirkannya ke objek, sebagai propertinya

- Cara termudah untuk melakukan ini adalah memodifikasi personobjek kosong , seperti:

person.Person = Person;
person.getName = getName;

dengan cara ini kita dapat memanggil mereka seperti:

person.Person("George");
person.getName();// -->"George"

dan sekarang personobjeknya seperti:

Object {Person: function, getName: function, name: "George"}

- Cara lain untuk melampirkan properti ke objek adalah menggunakan prototypeobjek yang dapat ditemukan di objek JavaScript apa pun dengan nama __proto__, dan saya telah mencoba menjelaskannya sedikit pada bagian ringkasan. Jadi kita bisa mendapatkan hasil yang serupa dengan melakukan:

person.__proto__.Person = Person;
person.__proto__.getName = getName;

Tapi dengan cara ini apa yang sebenarnya kita lakukan adalah memodifikasi Object.prototype, karena setiap kali kita membuat objek JavaScript menggunakan literal ( { ... }), itu akan dibuat berdasarkan Object.prototype, yang berarti akan dilampirkan ke objek yang baru dibuat sebagai atribut yang dinamai __proto__, jadi jika kita mengubahnya , seperti yang telah kami lakukan pada cuplikan kode kami sebelumnya, semua objek JavaScript akan berubah, bukan praktik yang baik. Jadi apa yang bisa menjadi praktik yang lebih baik sekarang:

person.__proto__ = {
    Person: Person,
    getName: getName
};

dan sekarang benda-benda lain dalam kedamaian, tetapi itu masih tampak bukan praktik yang baik. Jadi kita masih memiliki satu solusi lagi, tetapi untuk menggunakan solusi ini kita harus kembali ke baris kode tempat personobjek dibuat ( var person = {};) lalu ubah seperti:

var propertiesObject = {
    Person: Person,
    getName: getName
};
var person = Object.create(propertiesObject);

apa yang dilakukannya adalah menciptakan JavaScript baru Objectdan melampirkan propertiesObjectke __proto__atribut. Jadi untuk memastikan Anda bisa melakukan:

console.log(person.__proto__===propertiesObject); //true

Tetapi poin yang sulit di sini adalah Anda memiliki akses ke semua properti yang ditentukan __proto__pada tingkat pertama personobjek (baca bagian ringkasan untuk lebih detail).


seperti yang Anda lihat menggunakan salah satu dari dua cara thisini persis akan menunjuk ke personobjek.

c. JavaScript memiliki cara lain untuk menyediakan fungsi this, yaitu menggunakan panggilan atau menerapkan untuk memanggil fungsi.

Metode apply () memanggil fungsi dengan nilai ini dan argumen yang diberikan sebagai array (atau objek mirip array).

dan

Metode panggilan () memanggil fungsi dengan nilai ini dan argumen yang diberikan secara individual.

Dengan cara ini yang merupakan favorit saya, kami dapat dengan mudah memanggil fungsi kami seperti:

Person.call(person, "George");

atau

//apply is more useful when params count is not fixed
Person.apply(person, ["George"]);

getName.call(person);   
getName.apply(person);

3 metode ini adalah langkah awal yang penting untuk mengetahui fungsionalitas .prototype.


2- Bagaimana cara kerja newkata kunci?

ini adalah langkah kedua untuk memahami .prototypefungsi ini. Inilah yang saya gunakan untuk mensimulasikan proses:

function Person(name){  this.name = name;  }
my_person_prototype = { getName: function(){ console.log(this.name); } };

di bagian ini saya akan mencoba mengambil semua langkah yang diambil JavaScript, tanpa menggunakan newkata kunci dan prototype, ketika Anda menggunakan newkata kunci. jadi ketika kita melakukannya new Person("George"), Personfungsi berfungsi sebagai konstruktor, Inilah yang dilakukan JavaScript, satu per satu:

Sebuah. pertama-tama ia membuat objek kosong, pada dasarnya hash kosong seperti:

var newObject = {};

b. langkah selanjutnya yang diambil JavaScript adalah melampirkan semua objek prototipe ke objek yang baru dibuat

kami miliki di my_person_prototypesini mirip dengan objek prototipe.

for(var key in my_person_prototype){
    newObject[key] = my_person_prototype[key];
}

Ini bukan cara JavaScript benar-benar melampirkan properti yang didefinisikan dalam prototipe. Cara aktual terkait dengan konsep rantai prototipe.


Sebuah. & b. Alih-alih dua langkah ini Anda dapat memiliki hasil yang sama persis dengan melakukan:

var newObject = Object.create(my_person_prototype);
//here you can check out the __proto__ attribute
console.log(newObject.__proto__ === my_person_prototype); //true
//and also check if you have access to your desired properties
console.log(typeof newObject.getName);//"function"

sekarang kita dapat memanggil getNamefungsi di my_person_prototype:

newObject.getName();

c. maka itu memberikan objek itu ke konstruktor,

kita bisa melakukan ini dengan sampel kami seperti:

Person.call(newObject, "George");

atau

Person.apply(newObject, ["George"]);

maka constructor dapat melakukan apapun yang diinginkan, karena ini dalam konstruktor yang merupakan objek yang baru saja dibuat.

sekarang hasil akhirnya sebelum mensimulasikan langkah-langkah lain: Object {name: "George"}


Ringkasan:

Pada dasarnya, ketika Anda menggunakan kata kunci baru pada suatu fungsi, Anda memanggilnya dan fungsi itu berfungsi sebagai konstruktor, jadi ketika Anda mengatakan:

new FunctionName()

JavaScript internal membuat sebuah objek, sebuah hash kosong dan kemudian memberikan yang objek untuk konstruktor, maka konstruktor dapat melakukan apapun yang diinginkan, karena ini dalam konstruktor yang merupakan objek yang baru saja dibuat dan kemudian memberikan Anda bahwa objek tentu saja jika Anda belum menggunakan pernyataan pengembalian di fungsi Anda atau jika Anda telah meletakkan return undefined;di akhir fungsi Anda.

Jadi ketika JavaScript pergi untuk mencari properti pada suatu objek, hal pertama yang dilakukannya, adalah mencarinya di objek itu. Dan kemudian ada properti rahasia [[prototype]]yang biasanya kita suka __proto__dan properti itu adalah apa yang terlihat JavaScript selanjutnya. Dan ketika ia melihat melalui __proto__, sejauh itu lagi objek JavaScript lain, ia memiliki __proto__atribut sendiri , itu naik dan naik sampai ke titik di mana berikutnya __proto__adalah nol. Intinya adalah satu-satunya objek dalam JavaScript yang __proto__atributnya null adalah Object.prototypeobjek:

console.log(Object.prototype.__proto__===null);//true

dan itulah cara kerja warisan dalam JavaScript.

Rantai prototipe

Dengan kata lain, ketika Anda memiliki properti prototipe pada suatu fungsi dan Anda memanggil yang baru pada itu, setelah JavaScript selesai melihat objek yang baru dibuat untuk properti, itu akan melihat fungsi .prototypedan juga mungkin bahwa objek ini memiliki prototipe internal sendiri. dan seterusnya.


6
a) Tolong jangan jelaskan prototipe dengan menyalin properti b) Mengatur internal [[prototipe]] terjadi sebelum fungsi konstruktor diterapkan pada instance, silakan ubah urutan itu c) jQuery benar-benar offtopic dalam pertanyaan ini
Bergi

1
@Bergi: terima kasih telah menunjukkan, saya akan dihargai jika Anda memberi tahu saya jika itu tidak apa-apa sekarang.
Mehran Hatami

7
Bisakah Anda membuatnya sederhana? Anda benar dalam semua hal, tetapi siswa yang membaca penjelasan ini mungkin benar-benar bingung untuk pertama kalinya. ambil contoh yang lebih sederhana, dan biarkan kodenya menjelaskan sendiri atau tambahkan banyak komentar untuk memperjelas maksud Anda.
PM

2
@ PM: Terima kasih atas tanggapan Anda. Saya sudah mencoba membuatnya sesederhana mungkin tetapi saya pikir Anda benar, masih ada beberapa poin yang tidak jelas. Jadi saya akan mencoba memodifikasinya dan juga lebih deskriptif. :)
Mehran Hatami

1
@AndreaMattioli karena dengan cara ini Anda membuat objek yang sama sekali baru dan mengganti yang lama yang mungkin dibagikan di antara objek lain juga. Dengan mengganti __proto__Anda pertama-tama akan menghapus semua properti prototipe tingkat atas dan kemudian Anda memiliki basis proto baru yang tidak dibagikan lagi kecuali Anda membaginya.
Mehran Hatami

77

Tujuh Koans prototipe

Ketika Ciro San turun ke Gunung Rubah Api setelah meditasi mendalam, pikirannya jernih dan damai.

Namun tangannya, gelisah, dan dengan sendirinya meraih sikat dan mencatat catatan berikut.


0) Dua hal berbeda dapat disebut "prototipe":

  • properti prototipe, seperti pada obj.prototype

  • properti internal prototipe, dilambangkan seperti [[Prototype]] dalam ES5 .

    Itu dapat diambil melalui ES5 Object.getPrototypeOf().

    Firefox membuatnya dapat diakses melalui __proto__properti sebagai ekstensi. ES6 sekarang menyebutkan beberapa persyaratan opsional untuk __proto__.


1) Konsep-konsep itu ada untuk menjawab pertanyaan:

Ketika saya melakukannya obj.property, di mana JS mencari .property?

Secara intuitif, warisan klasik harus memengaruhi pencarian properti.


2)

  • __proto__digunakan untuk .pencarian properti dot seperti pada obj.property.
  • .prototypeyang tidak digunakan untuk pencarian langsung, hanya secara tidak langsung karena menentukan __proto__di pembuatan obyek dengan new.

Urutan pencarian adalah:

  • objproperti ditambahkan dengan obj.p = ...atauObject.defineProperty(obj, ...)
  • properti dari obj.__proto__
  • properti obj.__proto__.__proto__, dan sebagainya
  • jika beberapa __proto__adalah null, kembali undefined.

Inilah yang disebut rantai prototipe .

Anda dapat menghindari .pencarian dengan obj.hasOwnProperty('key')danObject.getOwnPropertyNames(f)


3) Ada dua cara utama untuk mengatur obj.__proto__:

  • new:

    var F = function() {}
    var f = new F()

    kemudian newtelah menetapkan:

    f.__proto__ === F.prototype

    Ini adalah di mana .prototypeakan digunakan.

  • Object.create:

     f = Object.create(proto)

    set:

    f.__proto__ === proto

4) Kode:

var F = function(i) { this.i = i }
var f = new F(1)

Sesuai dengan diagram berikut (beberapa Numberhal dihilangkan):

(Function)       (  F  )                                      (f)----->(1)
 |  ^             | | ^                                        |   i    |
 |  |             | | |                                        |        |
 |  |             | | +-------------------------+              |        |
 |  |constructor  | |                           |              |        |
 |  |             | +--------------+            |              |        |
 |  |             |                |            |              |        |
 |  |             |                |            |              |        |
 |[[Prototype]]   |[[Prototype]]   |prototype   |constructor   |[[Prototype]]
 |  |             |                |            |              |        |
 |  |             |                |            |              |        |
 |  |             |                | +----------+              |        |
 |  |             |                | |                         |        |
 |  |             |                | | +-----------------------+        |
 |  |             |                | | |                                |
 v  |             v                v | v                                |
(Function.prototype)              (F.prototype)                         |
 |                                 |                                    |
 |                                 |                                    |
 |[[Prototype]]                    |[[Prototype]]          [[Prototype]]|
 |                                 |                                    |
 |                                 |                                    |
 | +-------------------------------+                                    |
 | |                                                                    |
 v v                                                                    v
(Object.prototype)                                       (Number.prototype)
 | | ^
 | | |
 | | +---------------------------+
 | |                             |
 | +--------------+              |
 |                |              |
 |                |              |
 |[[Prototype]]   |constructor   |prototype
 |                |              |
 |                |              |
 |                | -------------+
 |                | |
 v                v |
(null)           (Object)

Diagram ini menunjukkan banyak node objek bahasa yang ditentukan sebelumnya:

  • null
  • Object
  • Object.prototype
  • Function
  • Function.prototype
  • 1
  • Number.prototype(dapat ditemukan dengan (1).__proto__, kurung wajib untuk memenuhi sintaksis)

2 baris kode kami hanya membuat objek baru berikut:

  • f
  • F
  • F.prototype

isekarang menjadi milik fkarena ketika Anda melakukannya:

var f = new F(1)

itu mengevaluasi Fdengan thismenjadi nilai yang newakan kembali, yang kemudian ditugaskan f.


5) .constructor biasanya berasal dari F.prototypemelalui .pencarian:

f.constructor === F
!f.hasOwnProperty('constructor')
Object.getPrototypeOf(f) === F.prototype
F.prototype.hasOwnProperty('constructor')
F.prototype.constructor === f.constructor

Saat kami menulis f.constructor, JavaScript melakukan .pencarian sebagai:

  • f tidak memiliki .constructor
  • f.__proto__ === F.prototypetelah .constructor === F, jadi ambillah

Hasilnya f.constructor == Fsecara intuitif benar, karena Fdigunakan untuk membangun f, misalnya mengatur bidang, seperti dalam bahasa OOP klasik.


6) Sintaks warisan klasik dapat dicapai dengan memanipulasi rantai prototipe.

ES6 menambahkan classdan extendskata kunci, yang sebagian besar sintaksis gula untuk kegilaan manipulasi prototipe yang sebelumnya mungkin.

class C {
    constructor(i) {
        this.i = i
    }
    inc() {
        return this.i + 1
    }
}

class D extends C {
    constructor(i) {
        super(i)
    }
    inc2() {
        return this.i + 2
    }
}
// Inheritance syntax works as expected.
c = new C(1)
c.inc() === 2
(new D(1)).inc() === 2
(new D(1)).inc2() === 3
// "Classes" are just function objects.
C.constructor === Function
C.__proto__ === Function.prototype
D.constructor === Function
// D is a function "indirectly" through the chain.
D.__proto__ === C
D.__proto__.__proto__ === Function.prototype
// "extends" sets up the prototype chain so that base class
// lookups will work as expected
var d = new D(1)
d.__proto__ === D.prototype
D.prototype.__proto__ === C.prototype
// This is what `d.inc` actually does.
d.__proto__.__proto__.inc === C.prototype.inc
// Class variables
// No ES6 syntax sugar apparently:
// http://stackoverflow.com/questions/22528967/es6-class-variable-alternatives
C.c = 1
C.c === 1
// Because `D.__proto__ === C`.
D.c === 1
// Nothing makes this work.
d.c === undefined

Diagram sederhana tanpa semua objek yang telah ditentukan:

(c)----->(1)
 |   i
 |
 |
 |[[Prototype]]
 |
 |
 v    __proto__
(C)<--------------(D)         (d)
| |                |           |
| |                |           |
| |prototype       |prototype  |[[Prototype]] 
| |                |           |
| |                |           |
| |                | +---------+
| |                | |
| |                | |
| |                v v
|[[Prototype]]    (D.prototype)--------> (inc2 function object)
| |                |             inc2
| |                |
| |                |[[Prototype]]
| |                |
| |                |
| | +--------------+
| | |
| | |
| v v
| (C.prototype)------->(inc function object)
|                inc
v
Function.prototype

Mari luangkan waktu sejenak untuk mempelajari cara kerja berikut ini:

c = new C(1)
c.inc() === 2

Garis set pertama c.iuntuk 1seperti yang dijelaskan dalam "4)".

Di baris kedua, ketika kita melakukannya:

c.inc()
  • .incditemukan melalui [[Prototype]]rantai: c-> C-> C.prototype->inc
  • ketika kita memanggil fungsi dalam Javascript X.Y(), JavaScript secara otomatis disetel thiske Xdalam Y()fungsi panggilan!

Logika yang sama persis juga menjelaskan d.incdan d.inc2.

Artikel ini https://javascript.info/class#not-just-a-syntax-sugar menyebutkan efek lebih lanjut yang classperlu diketahui. Beberapa dari mereka mungkin tidak dapat dicapai tanpa classkata kunci (TODO periksa yang mana):


1
@ ThomasB terima kasih! "Saya tidak tahu dari mana Anda mendapatkan ini": setelah saya melihat beberapa bahasa dinamis itu, saya perhatikan yang paling penting tentang sistem kelas mereka adalah cara kerja .pencarian (dan berapa banyak salinan data dibuat) . Jadi saya berusaha memahami hal itu. Sisanya adalah Google + posting blog + juru bahasa Js di tangan. :)
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

1
Saya masih belum mengerti mengapa g.constructor === Object karena Anda mengatakan bahwa "4) Ketika Anda melakukan f = new F, new juga menetapkan f.constructor = F". Bisakah Anda menjelaskan lebih banyak kepada saya? Bagaimanapun ini adalah jawaban terbaik yang saya cari. Terima kasih banyak!
nguyenngoc101

@ nguyenngoc101 terima kasih! Bagian sets f.constructor = Fitu jelas-jelas salah dan bertentangan dengan bagian selanjutnya: .constructorditemukan melalui .pencarian pada rantai prototipe. Perbaiki sekarang.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

dari semua diskusi apa yang saya dapatkan (berasal dari warisan klasik) jika saya membuat fungsi konstruktor dan mencoba untuk membuat contoh menggunakan operator baru saya hanya akan mendapatkan metode dan properti yang dilampirkan ke objek proto, oleh karena itu perlu untuk melampirkan semua metode dan properti ke objek proto jika kita ingin mewarisi, mi kan?
blackHawk

1
@CiroSantilli 刘晓波 死 六四 事件 法轮功 Saya pikir ini bukan bug di Chromium. Saya pikir itu hanya gejala bahwa fprototipe ditetapkan Fhanya pada waktu konstruksi; ftidak akan tahu atau peduli F.prototypekapan saja setelah pertama kali dibangun.
John Glassmyer

76

prototypememungkinkan Anda membuat kelas. jika Anda tidak menggunakannya prototypemaka itu menjadi statis.

Ini adalah contoh singkat.

var obj = new Object();
obj.test = function() { alert('Hello?'); };

Dalam kasus di atas, Anda memiliki tes panggilan fungsi statis. Fungsi ini hanya dapat diakses oleh obj.test di mana Anda dapat membayangkan obj menjadi sebuah kelas.

dimana seperti pada kode di bawah ini

function obj()
{
}

obj.prototype.test = function() { alert('Hello?'); };
var obj2 = new obj();
obj2.test();

Keberatan telah menjadi kelas yang sekarang bisa dipakai. Beberapa contoh objek bisa ada dan semuanya memiliki testfungsi.

Di atas adalah pemahaman saya. Saya menjadikannya sebagai wiki komunitas, sehingga orang dapat memperbaiki saya jika saya salah.


13
-1: prototypeadalah properti fungsi konstruktor, bukan instance, mis. Kode Anda salah! Mungkin Anda berarti properti non-standar __proto__objek, tapi itu binatang yang berbeda seluruh ...
Christoph

@Christoph - Terima kasih telah menunjukkannya. Saya telah memperbarui kode sampel.
Ramesh

3
Ada begitu banyak hal di dalamnya ... Ditambah JavaScript bukan bahasa berbasis kelas - ini berkaitan dengan warisan melalui prototipe, Anda perlu membahas perbedaan lebih detail!
James

5
Saya pikir jawaban ini agak salah arah.
Armin Cifuentes

Mungkin jawabannya adalah 'salah arah', tetapi menjelaskan prototipe apa yang digunakan dan sekarang semuanya jelas bagi saya, setelah semua 'jawaban' dengan suara ratusan 'ke atas'. Terima kasih.
Aleks

66

Setelah membaca utas ini, saya merasa bingung dengan Rantai Prototipe JavaScript, kemudian saya menemukan grafik ini

http://iwiki.readthedocs.org/en/latest/javascript/js_core.html#inheritance * [[protytype]] * dan <code> prototype </code> properti dari objek fungsi

ini bagan yang jelas untuk menunjukkan JavaScript Inheritance oleh Prototype Chain

dan

http://www.javascriptbank.com/javascript/article/JavaScript_Classical_Inheritance/

yang ini berisi contoh dengan kode dan beberapa diagram yang bagus.

rantai prototipe akhirnya jatuh kembali ke Object.prototype.

rantai prototipe dapat diperpanjang secara teknis selama yang Anda inginkan, setiap kali dengan menetapkan prototipe subkelas yang sama dengan objek kelas induk.

Semoga bermanfaat bagi Anda untuk memahami Rantai Prototipe JavaScript.


Apakah mungkin memiliki banyak pewarisan pada Javascript?

Apakah Foo objek literal di sini atau objek fungsi? Jika ini objek literal, saya percaya Foo.prototype tidak akan kembali ke Foo melalui konstruktor.
Madhur Ahuja

@ user3376708 JavaScript hanya mendukung satu warisan ( sumber )
Rafael Eyng

@ Nuno_147 Pada awalnya tidak jelas, tetapi jika Anda melihat cukup lama Anda mungkin mendapatkan sesuatu darinya.
marcelocra

3
Bisakah Anda menjelaskan apa [[Prototype]]artinya?
CodyBugstein

40

Setiap objek memiliki properti internal, [[Prototipe]] , yang menautkannya ke objek lain:

object [[Prototype]]  anotherObject

Dalam javascript tradisional, objek yang ditautkan adalah prototypeproperti dari suatu fungsi:

object [[Prototype]]  aFunction.prototype

Beberapa lingkungan mengekspos [[Prototipe]] sebagai __proto__:

anObject.__proto__ === anotherObject

Anda membuat tautan [[Prototipe]] saat membuat objek.

// (1) Object.create:
var object = Object.create(anotherObject)
// object.__proto__ = anotherObject

// (2) ES6 object initializer:
var object = { __proto__: anotherObject };
// object.__proto__ = anotherObject

// (3) Traditional JavaScript:
var object = new aFunction;
// object.__proto__ = aFunction.prototype

Jadi pernyataan ini setara:

var object = Object.create(Object.prototype);
var object = { __proto__: Object.prototype }; // ES6 only
var object = new Object;

Anda sebenarnya tidak dapat melihat target tautan ( Object.prototype) dalam pernyataan baru ; sebaliknya target tersirat oleh konstruktor (Object ).

Ingat:

  • Setiap objek memiliki tautan, [[Prototipe]] , terkadang ditampilkan sebagai __proto__ .
  • Setiap fungsi memiliki a prototype properti, awalnya memegang objek kosong.
  • Objek yang dibuat dengan yang baru ditautkan keprototype properti konstruktor mereka.
  • Jika suatu fungsi tidak pernah digunakan sebagai konstruktor, itu prototype propertinya akan tidak digunakan.
  • Jika Anda tidak memerlukan konstruktor, gunakan Object.create bukan new.

1
Revisi 5 menghapus beberapa info berguna, termasuk info tentang Object.create (). Lihat revisi 4 .
Palec

@Palec apa yang harus saya tambahkan kembali?
sam

2
IMO setidaknya tautan ke Object.create()dokumen , @sam. Tautan ke __proto__dan Object.prototypeakan menjadi perangkat tambahan yang bagus. Dan saya menyukai contoh Anda tentang bagaimana prototipe bekerja dengan konstruktor dan Object.create(), tetapi mereka mungkin bagian yang lama dan kurang relevan yang ingin Anda singkirkan.
Palec

dari semua diskusi apa yang saya dapatkan (berasal dari warisan klasik) jika saya membuat fungsi konstruktor dan mencoba untuk membuat contoh menggunakan operator baru saya hanya akan mendapatkan metode dan properti yang dilampirkan ke objek proto, oleh karena itu perlu untuk melampirkan semua metode dan properti ke objek proto jika kita ingin mewarisi, mi kan?
blackHawk

29

Javascript tidak memiliki warisan dalam arti biasa, tetapi memiliki rantai prototipe.

rantai prototipe

Jika anggota suatu objek tidak dapat ditemukan dalam objek itu akan dicari dalam rantai prototipe. Rantai terdiri dari benda-benda lain. Prototipe dari contoh yang diberikan dapat diakses dengan__proto__ variabel. Setiap objek memiliki satu, karena tidak ada perbedaan antara kelas dan instance dalam javascript.

Keuntungan menambahkan fungsi / variabel ke prototipe adalah bahwa ia harus berada di memori hanya sekali, bukan untuk setiap instance.

Ini juga berguna untuk warisan, karena rantai prototipe dapat terdiri dari banyak objek lain.


1
FF dan Chrome mendukung proto , tetapi tidak untuk IE atau Opera.
Beberapa

Georg, mohon klarifikasi untuk noob - "tidak ada perbedaan antara kelas dan instance dalam javascript." - bisakah kamu menguraikan? Bagaimana cara kerjanya?
Hamish Grubijan

dari semua diskusi apa yang saya dapatkan (berasal dari warisan klasik) jika saya membuat fungsi konstruktor dan mencoba untuk membuat contoh menggunakan operator baru saya hanya akan mendapatkan metode dan properti yang dilampirkan ke objek proto, oleh karena itu perlu untuk melampirkan semua metode dan properti ke objek proto jika kita ingin mewarisi, mi kan?
blackHawk

28

Artikel ini panjang. Tapi saya yakin itu akan menghapus sebagian besar pertanyaan Anda tentang sifat "prototipikal" dari JavaScript Inheritance. Dan bahkan lebih. Silakan baca artikel selengkapnya.

JavaScript pada dasarnya memiliki dua jenis tipe data

  • Bukan benda
  • Benda

Bukan benda

Berikut ini adalah tipe data non objek

  • tali
  • nomor (termasuk NaN dan Infinity)
  • nilai boolean (benar, salah)
  • tidak terdefinisi

Tipe data ini kembali mengikuti ketika Anda menggunakan operator typeof

typeof "string literal" (atau variabel yang berisi string literal) === 'string'

typeof 5 (atau numeric literal atau variabel yang mengandung numeric literal atau NaN atau Infinity ) === 'number'

typeof true (atau false atau variabel yang mengandung true atau false ) === 'boolean'

typeof undefined (atau variabel yang tidak terdefinisi atau variabel yang mengandung tidak terdefinisi ) === 'tidak terdefinisi'

Tipe data string , angka , dan boolean dapat direpresentasikan sebagai Objek dan Non objek . Saat direpresentasikan sebagai objek, tipenya selalu === 'objek'. Kami akan kembali ke ini setelah kami memahami tipe data objek.

Benda

Tipe data objek selanjutnya dapat dibagi menjadi dua jenis

  1. Objek tipe fungsi
  2. Jenis objek tidak berfungsi

The Fungsi jenis benda adalah orang-orang yang kembali string 'fungsi' dengan typeof operator. Semua fungsi yang ditentukan pengguna dan semua objek bawaan JavaScript yang dapat membuat objek baru dengan menggunakan operator baru termasuk dalam kategori ini. Untuk misalnya.

  • Obyek
  • Tali
  • Jumlah
  • Boolean
  • Himpunan
  • Array yang Diketik
  • RegExp
  • Fungsi
  • Semua objek bawaan lainnya yang dapat membuat objek baru dengan menggunakan operator baru
  • fungsi UserDefinedFunction () {/ * kode yang ditentukan pengguna * /}

Jadi, typeof (Object) === typeof (String) === typeof (Number) === typeof (Boolean) === typeof (Array) === typeof (RegExp) === typeof (Fungsi) == = typeof (UserDefinedFunction) === 'function'

Semua Fungsi jenis benda sebenarnya contoh yang dibangun di JavaScript objek Fungsi (termasuk Fungsi objek yaitu itu rekursif didefinisikan). Seolah-olah benda-benda ini telah didefinisikan dengan cara berikut

var Object= new Function ([native code for object Object])
var String= new Function ([native code for object String])
var Number= new Function ([native code for object Number])
var Boolean= new Function ([native code for object Boolean])
var Array= new Function ([native code for object Array])
var RegExp= new Function ([native code for object RegExp])
var Function= new Function ([native code  for object Function])
var UserDefinedFunction= new Function ("user defined code")

Seperti yang disebutkan, objek tipe Fungsi selanjutnya dapat membuat objek baru menggunakan operator baru . Untuk misalnya objek bertipe Objek , String , Nomor , Boolean , Array , RegExp Atau UserDefinedFunction dapat dibuat dengan menggunakan

var a=new Object() or var a=Object() or var a={} //Create object of type Object
var a=new String() //Create object of type String
var a=new Number() //Create object of type Number
var a=new Boolean() //Create object of type Boolean
var a=new Array() or var a=Array() or var a=[]  //Create object of type Array
var a=new RegExp() or var a=RegExp() //Create object of type RegExp
var a=new UserDefinedFunction() 

Objek yang dibuat adalah semua objek tipe Non Function dan mengembalikan tipenya === 'objek' . Dalam semua kasus ini objek "a" tidak dapat lagi membuat objek menggunakan operator baru. Jadi yang berikut ini salah

var b=new a() //error. a is not typeof==='function'

Objek matematika bawaan adalah typeof === 'objek' . Oleh karena itu objek baru tipe Math tidak dapat dibuat oleh operator baru.

var b=new Math() //error. Math is not typeof==='function'

Perhatikan juga bahwa fungsi Object , Array dan RegExp dapat membuat objek baru tanpa menggunakan operator baru . Namun yang mengikuti tidak.

var a=String() // Create a new Non Object string. returns a typeof==='string' 
var a=Number() // Create a new Non Object Number. returns a typeof==='number'
var a=Boolean() //Create a new Non Object Boolean. returns a typeof==='boolean'

Fungsi yang ditentukan pengguna adalah case khusus.

var a=UserDefinedFunction() //may or may not create an object of type UserDefinedFunction() based on how it is defined.

Karena objek tipe Fungsi dapat membuat objek baru, mereka juga disebut Konstruktor .

Setiap Konstruktor / Fungsi (baik bawaan atau yang ditentukan pengguna) saat didefinisikan secara otomatis memiliki properti yang disebut "prototipe" yang nilainya secara default ditetapkan sebagai objek. Objek ini sendiri memiliki properti yang disebut "konstruktor" yang secara default merujuk kembali kepada Konstruktor / Fungsi .

Misalnya ketika kita mendefinisikan suatu fungsi

function UserDefinedFunction()
{
}

berikut secara otomatis terjadi

UserDefinedFunction.prototype={constructor:UserDefinedFunction}

Properti "prototipe" ini hanya ada di objek tipe Fungsi (dan tidak pernah di objek tipe Fungsi ).

Ini karena ketika objek baru dibuat (menggunakan operator baru) ia mewarisi semua properti dan metode dari objek prototipe fungsi Constructor saat ini yaitu referensi internal dibuat dalam objek yang baru dibuat yang mereferensikan objek yang dirujuk oleh objek prototipe fungsi Constructor saat ini.

Ini "referensi internal" yang dibuat dalam objek untuk referensi properti diwariskan dikenal sebagai prototipe objek (yang referensi objek direferensikan oleh Konstruktor "prototipe" properti tetapi berbeda dari itu). Untuk objek apa pun (Fungsi atau Tidak Fungsi) ini dapat diambil menggunakan metode Object.getPrototypeOf () . Menggunakan metode ini orang dapat melacak rantai prototipe suatu objek.

Juga, setiap objek yang dibuat ( tipe Fungsi atau tipe Non Fungsi ) memiliki properti "konstruktor" yang diwarisi dari objek yang dirujuk oleh properti prototipe fungsi Konstruktor. Secara default, properti "konstruktor" ini merujuk fungsi Konstruktor yang membuatnya (jika prototipe " Fungsi " default Constructor tidak diubah).

Untuk semua objek tipe Fungsi , fungsi konstruktor selalu berfungsi Function () {}

Untuk objek tipe Non Function (mis. Javascript Built in Math object) fungsi konstruktor adalah fungsi yang membuatnya. Untuk objek Math, ini adalah function Object () {} .

Semua konsep yang dijelaskan di atas dapat sedikit menakutkan untuk dipahami tanpa kode pendukung. Silakan pergi melalui baris kode berikut untuk memahami konsep. Cobalah untuk mengeksekusinya agar memiliki pemahaman yang lebih baik.

function UserDefinedFunction()
{ 

} 

/* creating the above function automatically does the following as mentioned earlier

UserDefinedFunction.prototype={constructor:UserDefinedFunction}

*/


var newObj_1=new UserDefinedFunction()

alert(Object.getPrototypeOf(newObj_1)===UserDefinedFunction.prototype)  //Displays true

alert(newObj_1.constructor) //Displays function UserDefinedFunction

//Create a new property in UserDefinedFunction.prototype object

UserDefinedFunction.prototype.TestProperty="test"

alert(newObj_1.TestProperty) //Displays "test"

alert(Object.getPrototypeOf(newObj_1).TestProperty)// Displays "test"

//Create a new Object

var objA = {
        property1 : "Property1",
        constructor:Array

}


//assign a new object to UserDefinedFunction.prototype
UserDefinedFunction.prototype=objA

alert(Object.getPrototypeOf(newObj_1)===UserDefinedFunction.prototype)  //Displays false. The object referenced by UserDefinedFunction.prototype has changed

//The internal reference does not change
alert(newObj_1.constructor) // This shall still Display function UserDefinedFunction

alert(newObj_1.TestProperty) //This shall still Display "test" 

alert(Object.getPrototypeOf(newObj_1).TestProperty) //This shall still Display "test"


//Create another object of type UserDefinedFunction
var newObj_2= new UserDefinedFunction();

alert(Object.getPrototypeOf(newObj_2)===objA) //Displays true.

alert(newObj_2.constructor) //Displays function Array()

alert(newObj_2.property1) //Displays "Property1"

alert(Object.getPrototypeOf(newObj_2).property1) //Displays "Property1"

//Create a new property in objA
objA.property2="property2"

alert(objA.property2) //Displays "Property2"

alert(UserDefinedFunction.prototype.property2) //Displays "Property2"

alert(newObj_2.property2) // Displays Property2

alert(Object.getPrototypeOf(newObj_2).property2) //Displays  "Property2"

Rantai prototipe setiap objek akhirnya melacak kembali ke Object.prototype (yang itu sendiri tidak memiliki objek prototipe). Kode berikut dapat digunakan untuk melacak rantai prototipe suatu objek

var o=Starting object;

do {
    alert(o + "\n" + Object.getOwnPropertyNames(o))

}while(o=Object.getPrototypeOf(o))

Rantai prototipe untuk berbagai objek bekerja sebagai berikut.

  • Setiap objek Function (termasuk objek Function bawaan) -> Function.prototype -> Object.prototype -> null
  • Objek Sederhana (dibuat oleh Objek baru () atau {} termasuk objek matematika bawaan) -> Object.prototype -> null
  • Objek dibuat dengan yang baru atau Object.create -> Satu atau lebih rantai prototipe -> Object.prototype -> null

Untuk membuat objek tanpa prototipe gunakan yang berikut ini:

var o=Object.create(null)
alert(Object.getPrototypeOf(o)) //Displays null

Orang mungkin berpikir bahwa menyetel properti prototipe Konstruktor ke nol harus membuat objek dengan prototipe nol. Namun dalam kasus seperti itu prototipe objek yang baru dibuat diatur ke Object.prototype dan konstruktornya diatur untuk berfungsi Object. Ini ditunjukkan oleh kode berikut

function UserDefinedFunction(){}
UserDefinedFunction.prototype=null// Can be set to any non object value (number,string,undefined etc.)

var o=new UserDefinedFunction()
alert(Object.getPrototypeOf(o)==Object.prototype)   //Displays true
alert(o.constructor)    //Displays Function Object

Berikut ringkasan artikel ini

  • Ada dua jenis objek Jenis fungsi dan tipe Non Fungsi
  • Hanya objek tipe Fungsi yang dapat membuat objek baru menggunakan operator baru . Objek yang dibuat adalah objek tipe Non Function . The Non Fungsi jenis benda tidak dapat lebih membuat objek menggunakan operator baru .

  • Semua objek tipe fungsi secara default memiliki properti "prototipe" . Properti "prototipe" ini merujuk objek yang memiliki properti "konstruktor" yang secara default mereferensikan objek tipe Function itu sendiri.

  • Semua objek ( tipe Fungsi dan tipe Non Fungsi ) memiliki properti "konstruktor" yang secara default mereferensikan objek tipe Fungsi / Konstruktor yang membuatnya.

  • Setiap objek yang dibuat secara internal mereferensikan objek yang dirujuk oleh properti "prototype" dari Constructor yang membuatnya. Objek ini dikenal sebagai prototipe objek yang dibuat (yang berbeda dari objek Tipe fungsi "prototipe" properti yang dirujuk). Dengan cara ini objek yang dibuat dapat secara langsung mengakses metode dan properti yang didefinisikan dalam objek yang dirujuk oleh properti "prototipe" Constructor (pada saat pembuatan objek).

  • Prototipe suatu objek (dan karenanya nama properti warisannya) dapat diambil menggunakan metode Object.getPrototypeOf () . Bahkan metode ini dapat digunakan untuk menavigasi seluruh rantai prototipe objek.

  • Rantai prototipe setiap objek akhirnya melacak kembali ke Object.prototype (Kecuali objek dibuat menggunakan Object.create (null) dalam hal objek tidak memiliki prototipe).

  • typeof (new Array ()) === 'objek' adalah dengan desain bahasa dan bukan kesalahan seperti yang ditunjukkan oleh Douglas Crockford

  • Menetapkan properti prototipe dari Konstruktor ke null (atau tidak terdefinisi, angka, benar, salah, string) tidak boleh membuat objek dengan prototipe nol. Dalam kasus seperti itu prototipe objek yang baru dibuat diatur ke Object.prototype dan konstruktornya diatur untuk berfungsi Object.

Semoga ini membantu.


24

Konsep prototypalpewarisan adalah salah satu yang paling rumit bagi banyak pengembang. Mari kita coba memahami akar masalah untuk memahami dengan prototypal inheritancelebih baik. Mari kita mulai dengan suatu plainfungsi.

masukkan deskripsi gambar di sini

Jika kami menggunakan newoperator pada Tree function, kami menyebutnya sebagai constructorfungsi.

masukkan deskripsi gambar di sini

Setiap JavaScriptfungsi memiliki a prototype. Saat Anda masuk Tree.prototype, Anda mendapatkan ...

masukkan deskripsi gambar di sini

Jika Anda melihat console.log()output di atas , Anda bisa melihat properti konstruktor Tree.prototypedan __proto__properti juga. Yang __proto__menyatakan prototypebahwa ini functiondidasarkan, dan karena ini hanyalah sebuah dataran JavaScript functiontanpa inheritancepengaturan, itu mengacu pada Object prototypeyang merupakan sesuatu yang baru saja dibangun di dalam JavaScript ...

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/prototype

Ini memiliki hal-hal seperti .toString, .toValue, .hasOwnPropertydll ...

__proto__yang dibawa mozilla saya sudah usang dan digantikan oleh Object.getPrototypeOfmetode untuk mendapatkan object's prototype.

masukkan deskripsi gambar di sini

Object.getPrototypeOf(Tree.prototype); // Object {} 

Mari kita tambahkan metode ke Tree prototype.

masukkan deskripsi gambar di sini

Kami telah memodifikasi Rootdan menambahkan functioncabang ke sana.

masukkan deskripsi gambar di sini

Itu berarti ketika Anda membuat instancedari Tree, Anda dapat menyebutnya ini branchmetode.

masukkan deskripsi gambar di sini

Kami juga dapat menambahkan primitivesatau objectske Prototype.

masukkan deskripsi gambar di sini

Mari tambahkan child-treeke Tree.

masukkan deskripsi gambar di sini

Di sini Childmewarisi prototypedari Tree, apa yang kita lakukan di sini menggunakan Object.create()metode untuk membuat objek baru berdasarkan apa yang Anda lewati, ini dia Tree.prototype. Dalam hal ini yang kami lakukan adalah mengatur prototipe Child ke objek baru yang terlihat identik dengan Treeprototipe. Selanjutnya kita sedang mengatur Child's constructor to Child, jika tidak, itu akan menunjuk ke Tree().

masukkan deskripsi gambar di sini

Childsekarang memiliki sendiri prototype, __proto__poin ke Treedan Tree's prototypepoin ke pangkalan Object.

Child  
|
 \
  \
   Tree.prototype
   - branch
   |
   |
    \
     \
      Object.prototype
      -toString
      -valueOf
      -etc., etc.

Sekarang Anda membuat instancedari Childdan panggilan branchyang awalnya tersedia dalam Tree. Kami belum benar - benar mendefinisikan kami branchdi Child prototype. TETAPI, di Root prototypemana Anak diwarisi dari.

masukkan deskripsi gambar di sini

Dalam JS semuanya bukan objek, semuanya bisa bertindak seperti objek.

Javascriptmemiliki primitif seperti strings, number, booleans, undefined, null.Mereka tidak object(i.e reference types), tetapi tentu saja dapat bertindak seperti object. Mari kita lihat contoh di sini.

masukkan deskripsi gambar di sini

Di baris pertama cantuman ini, primitivenilai string ditetapkan ke nama. Baris kedua memperlakukan nama seperti objectdan memanggil charAt(0)menggunakan notasi titik.

Inilah yang terjadi di balik layar: // apa yang JavaScriptdilakukan mesin

masukkan deskripsi gambar di sini

The String objectada hanya untuk satu pernyataan sebelum itu hancur (suatu proses yang disebut autoboxing). Mari kita kembali ke kita prototypal inheritance.

  • Javascriptmendukung warisan melalui delegationberbasis pada prototypes.
  • Masing-masing Functionmemiliki prototypeproperti, yang merujuk ke objek lain.
  • properties/functionsdilihat dari objectdirinya sendiri atau melalui prototyperantai jika tidak ada

A prototypedalam JS adalah objek yang yieldsAnda ke induk dari yang lain object. [mis. delegasi] Delegation berarti bahwa jika Anda tidak dapat melakukan sesuatu, Anda akan memberitahu orang lain untuk melakukannya untuk Anda.

masukkan deskripsi gambar di sini

https://jsfiddle.net/say0tzpL/1/

Jika Anda melihat biola di atas, anjing memiliki akses ke toStringmetode, tetapi itu tidak tersedia di dalamnya, tetapi tersedia melalui rantai prototipe yang didelegasikan kepadaObject.prototype

masukkan deskripsi gambar di sini

Jika Anda melihat yang di bawah ini, kami mencoba mengakses callmetode yang tersedia di setiap function.

masukkan deskripsi gambar di sini

https://jsfiddle.net/rknffckc/

Jika Anda mencari biola di atas, ProfileFunction memiliki akses ke callmetode, tetapi itu tidak tersedia di dalamnya, tetapi tersedia melalui rantai prototipe yang didelegasikan kepadaFunction.prototype

masukkan deskripsi gambar di sini

Catatan: prototype adalah properti dari konstruktor fungsi, sedangkan __proto__adalah properti dari objek yang dibangun dari konstruktor fungsi. Setiap fungsi dilengkapi dengan prototypeproperti yang nilainya kosong object. Ketika kita membuat turunan dari fungsi, kita mendapatkan properti internal [[Prototype]]atau __proto__yang referensi adalah prototipe dari Fungsi constructor.

masukkan deskripsi gambar di sini

Diagram di atas terlihat agak rumit, tetapi menampilkan seluruh gambaran tentang cara prototype chainingkerjanya. Mari kita berjalan perlahan-lahan:

Ada dua contoh b1dan b2, yang konstruktornya Bardan induknya adalah Foo dan memiliki dua metode dari rantai prototipe identifydan speakvia BardanFoo

masukkan deskripsi gambar di sini

https://jsfiddle.net/kbp7jr7n/

Jika Anda melihat kode di atas, kami memiliki Fookonstruktor yang memiliki metode identify()dan Barkonstruktor yang memiliki speakmetode. Kami membuat dua Barcontoh b1dan b2yang jenis induknya Foo. Sekarang saat memanggil speakmetode Bar, kami dapat mengidentifikasi siapa yang memanggil bicara melalui prototyperantai.

masukkan deskripsi gambar di sini

Barsekarang memiliki semua metode Fooyang didefinisikan di dalamnya prototype. Mari kita menggali lebih jauh dalam memahami Object.prototypedan Function.prototypedan bagaimana mereka terkait. Jika Anda mencari konstruktor Foo, Bardan Objectsedang Function constructor.

masukkan deskripsi gambar di sini

The prototypedari Barini Foo, prototypedari Foois Objectdan jika Anda melihat dekat yang prototypedari Fooberhubungan dengan Object.prototype.

masukkan deskripsi gambar di sini

Sebelum kita menutup ini, mari kita bungkus dengan sepotong kecil kode di sini untuk meringkas semua yang di atas . Kami menggunakan instanceofOperator di sini untuk memeriksa apakah objecttelah di-nya prototyperantai prototypeproperti dari constructoryang di bawah merangkum seluruh diagram besar.

masukkan deskripsi gambar di sini

Saya harap ini menambahkan beberapa informasi, saya tahu ini agak bisa menjadi besar untuk dipahami ... dengan kata sederhana itu hanya benda yang terhubung ke objek !!!!


22

apa tujuan sebenarnya dari properti ".prototype" ini?

Antarmuka ke kelas standar menjadi dapat diperluas. Misalnya, Anda menggunakan Arraykelas dan Anda juga perlu menambahkan serializer khusus untuk semua objek array Anda. Apakah Anda menghabiskan waktu mengkodekan subclass, atau menggunakan komposisi atau ... Properti prototipe menyelesaikan ini dengan membiarkan pengguna mengontrol set anggota / metode yang tersedia untuk kelas.

Pikirkan prototipe sebagai pointer-vtable tambahan. Ketika beberapa anggota hilang dari kelas asli, prototipe tersebut dilihat saat runtime.


21

Mungkin membantu untuk mengkategorikan rantai prototipe menjadi dua kategori.

Pertimbangkan konstruktor:

 function Person() {}

Nilai Object.getPrototypeOf(Person)adalah fungsi. Faktanya, itu benar Function.prototype. Karena Persondibuat sebagai fungsi, ia berbagi objek fungsi prototipe yang sama dengan semua fungsi. Itu sama dengan Person.__proto__, tetapi properti itu tidak boleh digunakan. Bagaimanapun, dengan Object.getPrototypeOf(Person)Anda berjalan secara efektif menaiki tangga dari apa yang disebut rantai prototipe.

Rantai ke arah atas terlihat seperti ini:

    PersonFunction.prototypeObject.prototype(titik akhir)

Penting adalah bahwa rantai prototipe ini tidak ada hubungannya dengan objek yang Persondapat dikonstruksi . Benda-benda yang dibangun memiliki rantai prototipe sendiri, dan rantai ini berpotensi tidak memiliki leluhur yang sama dengan yang disebutkan di atas.

Ambil contoh objek ini:

var p = new Person();

p tidak memiliki hubungan prototipe-rantai langsung dengan Person . Hubungan mereka berbeda. Objek p memiliki rantai prototipe sendiri. Menggunakan Object.getPrototypeOf, Anda akan menemukan rantai sebagai berikut:

    pPerson.prototypeObject.prototype(titik akhir)

Tidak ada objek fungsi dalam rantai ini (meskipun itu bisa).

Jadi Personsepertinya terkait dengan dua jenis rantai, yang menjalani hidup mereka sendiri. Untuk "melompat" dari satu rantai ke rantai lain, Anda menggunakan:

  1. .prototype: melompat dari rantai konstruktor ke rantai objek-yang dibuat. Properti ini hanya didefinisikan untuk objek fungsi (karena newhanya dapat digunakan pada fungsi).

  2. .constructor: melompat dari rantai objek yang dibuat ke rantai konstruktor.

Berikut ini adalah presentasi visual dari dua rantai prototipe yang terlibat, direpresentasikan sebagai kolom:

masukkan deskripsi gambar di sini

Untuk meringkas:

The prototypeproperti tidak memberikan informasi dari subjek rantai prototipe, namun benda yang diciptakan oleh subjek.

Tidak mengherankan bahwa nama properti prototypedapat menyebabkan kebingungan. Mungkin akan lebih jelas jika properti ini dinamai prototypeOfConstructedInstancesatau sesuatu di sepanjang garis itu.

Anda dapat melompat-lompat di antara dua rantai prototipe:

Person.prototype.constructor === Person

Simetri ini dapat dipecah dengan secara eksplisit menetapkan objek berbeda ke prototypeproperti (lebih lanjut tentang itu nanti).

Buat satu Fungsi, Dapatkan Dua Objek

Person.prototypeadalah objek yang dibuat bersamaan dengan fungsi Personyang dibuat. Memiliki Personsebagai konstruktor, meskipun konstruktor itu belum benar-benar mengeksekusi. Jadi dua objek dibuat secara bersamaan:

  1. Fungsinya Personsendiri
  2. Objek yang akan bertindak sebagai prototipe ketika fungsinya disebut sebagai konstruktor

Keduanya adalah objek, tetapi mereka memiliki peran yang berbeda: objek fungsi membangun , sedangkan objek lainnya mewakili prototipe objek yang akan dibangun oleh fungsi. Objek prototipe akan menjadi induk dari objek yang dibangun dalam rantai prototipe.

Karena fungsi juga merupakan objek, ia juga memiliki induknya sendiri dalam rantai prototipenya sendiri, tetapi ingat bahwa kedua rantai ini adalah tentang hal-hal yang berbeda.

Berikut adalah beberapa persamaan yang dapat membantu memahami masalah ini - semua cetak ini true:

function Person() {};

// This is prototype chain info for the constructor (the function object):
console.log(Object.getPrototypeOf(Person) === Function.prototype);
// Step further up in the same hierarchy:
console.log(Object.getPrototypeOf(Function.prototype) === Object.prototype);
console.log(Object.getPrototypeOf(Object.prototype) === null);
console.log(Person.__proto__ === Function.prototype);
// Here we swap lanes, and look at the constructor of the constructor
console.log(Person.constructor === Function);
console.log(Person instanceof Function);

// Person.prototype was created by Person (at the time of its creation)
// Here we swap lanes back and forth:
console.log(Person.prototype.constructor === Person);
// Although it is not an instance of it:
console.log(!(Person.prototype instanceof Person));
// Instances are objects created by the constructor:
var p = new Person();
// Similarly to what was shown for the constructor, here we have
// the same for the object created by the constructor:
console.log(Object.getPrototypeOf(p) === Person.prototype);
console.log(p.__proto__ === Person.prototype);
// Here we swap lanes, and look at the constructor
console.log(p.constructor === Person);
console.log(p instanceof Person);

Menambahkan level ke rantai prototipe

Meskipun objek prototipe dibuat ketika Anda membuat fungsi konstruktor, Anda dapat mengabaikan objek itu, dan menetapkan objek lain yang harus digunakan sebagai prototipe untuk setiap kejadian selanjutnya yang dibuat oleh konstruktor itu.

Contohnya:

function Thief() { }
var p = new Person();
Thief.prototype = p; // this determines the prototype for any new Thief objects:
var t = new Thief();

Sekarang rantai prototipe t adalah satu langkah lebih lama dari pada p :

    tpPerson.prototypeObject.prototype(titik akhir)

Rantai prototipe lain tidak lagi: Thiefdan Personsaudara kandung berbagi orangtua yang sama dalam rantai prototipe mereka:

    Person}
    Thief  } → Function.prototypeObject.prototype(titik akhir)

Grafik yang disajikan sebelumnya dapat diperpanjang ke ini (aslinya Thief.prototypeditinggalkan):

masukkan deskripsi gambar di sini

Garis biru mewakili rantai prototipe, garis berwarna lainnya mewakili hubungan lain:

  • antara objek dan konstruktornya
  • antara konstruktor dan objek prototipe yang akan digunakan untuk membangun objek


16

Saya merasa bermanfaat untuk menjelaskan "rantai prototipe" sebagai konvensi rekursif ketika obj_n.prop_Xdirujuk:

jika obj_n.prop_Xtidak ada, periksa di obj_n+1.prop_Xmanaobj_n+1 = obj_n.[[prototype]]

Jika prop_Xakhirnya ditemukan dalam objek prototipe k-th maka

obj_1.prop_X = obj_1.[[prototype]].[[prototype]]..(k-times)..[[prototype]].prop_X

Anda dapat menemukan grafik hubungan objek Javascript dengan propertinya di sini:

objek js grafik

http://jsobjects.org


14

Ketika konstruktor membuat objek, objek tersebut secara implisit mereferensikan properti "prototipe" konstruktor untuk tujuan menyelesaikan referensi properti. Properti "prototipe" konstruktor dapat dirujuk oleh ekspresi program constructor.prototype, dan properti yang ditambahkan ke prototipe objek dibagikan, melalui warisan, oleh semua objek yang berbagi prototipe.


11

Ada dua entitas berbeda namun terkait di sini yang perlu dijelaskan:

  • The .prototypeproperti fungsi.
  • Properti [[Prototype]][1] dari semua objek [2] .

Ini adalah dua hal yang berbeda.

The [[Prototype]]properti:

Ini adalah properti yang ada di semua [2] objek.

Apa yang disimpan di sini adalah objek lain, yang, sebagai objek itu sendiri, memiliki objeknya sendiri [[Prototype]]yang menunjuk ke objek lain. Objek lain itu memiliki objeknya [[Prototype]]sendiri. Kisah ini berlanjut hingga Anda mencapai objek prototipikal yang menyediakan metode yang dapat diakses pada semua objek (seperti .toString).

The [[Prototype]]properti adalah bagian dari apa membentuk[[Prototype]] rantai. Rantai [[Prototype]]objek ini adalah apa yang diperiksa ketika, misalnya, [[Get]]atau [[Set]]operasi dilakukan pada objek:

var obj = {}
obj.a         // [[Get]] consults prototype chain
obj.b = 20    // [[Set]] consults prototype chain

The .prototypeproperti:

Ini adalah properti yang hanya ditemukan di fungsi.Menggunakan fungsi yang sangat sederhana:

function Bar(){};

The .prototypeproperti memegang sebuah benda yang akan ditugaskan untukb.[[Prototype]] ketika Anda melakukan var b = new Bar. Anda dapat dengan mudah memeriksa ini:

// Both assign Bar.prototype to b1/b2[[Prototype]]
var b = new Bar;
// Object.getPrototypeOf grabs the objects [[Prototype]]
console.log(Object.getPrototypeOf(b) === Bar.prototype) // true

Salah satu yang paling penting .prototypeadalah bahwa dari Objectfungsi . Prototipe ini memiliki objek prototipikal yang [[Prototype]]berisi semua rantai. Di atasnya, semua metode yang tersedia untuk objek baru didefinisikan:

// Get properties that are defined on this object
console.log(Object.getOwnPropertyDescriptors(Object.prototype))

Sekarang, karena .prototypemerupakan objek, ia memiliki [[Prototype]]properti. Ketika Anda tidak membuat tugas apa pun untukFunction.prototype , .prototype's [[Prototype]]menunjuk ke objek prototipikal ( Object.prototype). Ini dilakukan secara otomatis kapan pun Anda membuat fungsi baru.

Dengan cara ini, setiap kali Anda melakukan new Bar;rantai prototipe diatur untuk Anda, Anda mendapatkan semua yang ditetapkan Bar.prototypedan semuanya ditetapkanObject.prototype :

var b = new Bar;
// Get all Bar.prototype properties
console.log(b.__proto__ === Bar.prototype)
// Get all Object.prototype properties
console.log(b.__proto__.__proto__ === Object.prototype)

Kapan kamu melakukan tugas make untuk Function.prototypesemua yang Anda lakukan adalah memperpanjang rantai prototipe untuk menyertakan objek lain. Ini seperti penyisipan dalam daftar yang terhubung sendiri.

Ini pada dasarnya mengubah [[Prototype]] rantai yang memungkinkan properti yang didefinisikan pada objek yang ditugaskan untuk Function.prototypedilihat oleh objek apa pun yang dibuat oleh fungsi.


[1: Itu tidak akan membingungkan siapa pun; disediakan melalui satu __proto__properti di banyak implementasi.
[2]: Semua kecuali null.


10

Biarkan saya memberi tahu Anda pemahaman saya tentang prototipe. Saya tidak akan membandingkan warisan di sini dengan bahasa lain. Saya berharap orang-orang berhenti membandingkan bahasa, dan hanya memahami bahasa itu sendiri. Memahami prototipe dan pewarisan prototipe sangat sederhana, seperti yang akan saya tunjukkan di bawah ini.

Prototipe seperti model, berdasarkan mana Anda membuat produk. Poin penting untuk dipahami adalah bahwa ketika Anda membuat objek menggunakan objek lain sebagai prototipe, tautan antara prototipe dan produk tersebut akan bertahan lama. Contohnya:

var model = {x:2};
var product = Object.create(model);
model.y = 5;
product.y
=>5

Setiap objek berisi properti internal yang disebut [[prototipe]], yang dapat diakses oleh Object.getPrototypeOf()fungsinya. Object.create(model)membuat objek baru dan menyetel properti [[prototipe]] ke model objek . Maka ketika Anda melakukannya Object.getPrototypeOf(product), Anda akan mendapatkan model objek .

Properti dalam produk ditangani dengan cara berikut:

  • Ketika sebuah properti diakses untuk hanya membaca nilainya, properti itu terlihat di rantai lingkup. Pencarian untuk variabel dimulai dari produk ke atas ke prototipe itu. Jika variabel seperti itu ditemukan dalam pencarian, pencarian dihentikan di sana, dan nilainya dikembalikan. Jika variabel semacam itu tidak dapat ditemukan dalam rantai lingkup, undefined dikembalikan.
  • Ketika suatu properti ditulis (diubah), maka properti itu selalu ditulis pada objek produk . Jika produk belum memiliki properti seperti itu, itu secara implisit dibuat dan ditulis.

Seperti menghubungkan objek menggunakan properti prototipe disebut pewarisan prototypal. Itu, sangat sederhana, setuju?


Tidak selalu ditulis pada produk saat penugasan. Anda tidak memperjelas bahwa anggota spesifik contoh harus diinisialisasi dan anggota yang dibagikan dapat menggunakan prototipe. Terutama ketika Anda memiliki anggota yang dapat berubah yang dapat instance: stackoverflow.com/questions/16063394/...
HMR

HMR: Dalam contoh Anda dalam jawaban Anda, ben.food.push ("Hamburger"); baris mengubah properti objek prototipe karena hal berikut: 1.) Pertama ben.food melihat ke atas, dan setiap tindakan pencarian hanya akan mencari rantai lingkup. 2.) Fungsi push objek ben.food dieksekusi. Dengan menulis mode dalam jawaban saya, maksud saya ketika Anda secara eksplisit menetapkan nilai untuk itu, seperti pada: ben.food = ['Idly']; Ini akan selalu membuat properti baru (jika belum ada di sana) pada objek produk, dan kemudian menetapkan nilainya.
Aravind

HMR: Terima kasih atas komentar Anda, itu membuat saya berpikir dan menguji pemahaman saya.
Aravind

Ketika menetapkan ulang ben.food itu akan membayangi anggota makanan kecuali makanan dibuat menggunakan Object.defineProperty, Object.defineProperties atau Object.create dengan argumen kedua (jadi tidak selalu). Anda bahkan dapat mengubah prototipe dengan (apa yang tampak seperti) tugas ulang ketika Anda membuat pengambil pengambil. Ketika datang ke pola pewarisan saya mengerti fungsi konstruktor sulit untuk dipahami dan memiliki beberapa masalah besar tetapi itu baik jika Anda memahaminya. Warisan dalam JavaScript tidak dimulai dan diakhiri dengan pengaturan prototipe, inisialisasi (konstruktor) juga harus digunakan (kembali).
HMR

Jawaban Anda bagus dalam menjelaskan prototipe tetapi bisa salah ditafsirkan dengan menyederhanakan pewarisan dalam JavaScript dan contoh anggota tertentu. Banyak pertanyaan yang telah diajukan mengapa mutasi anggota prototipe pada instance memengaruhi instance lain.
HMR


10

Pertimbangkan keyValueStoreobjek berikut :

var keyValueStore = (function() {
    var count = 0;
    var kvs = function() {
        count++;
        this.data = {};
        this.get = function(key) { return this.data[key]; };
        this.set = function(key, value) { this.data[key] = value; };
        this.delete = function(key) { delete this.data[key]; };
        this.getLength = function() {
            var l = 0;
            for (p in this.data) l++;
            return l;
        }
    };

    return  { // Singleton public properties
        'create' : function() { return new kvs(); },
        'count' : function() { return count; }
    };
})();

Saya dapat membuat instance baru dari objek ini dengan melakukan ini:

kvs = keyValueStore.create();

Setiap instance objek ini akan memiliki properti publik berikut:

  • data
  • get
  • set
  • delete
  • getLength

Sekarang, misalkan kita membuat 100 instance dari keyValueStoreobjek ini . Meskipun get, set,delete ,getLength akan melakukan hal yang sama persis untuk setiap 100 kasus ini, setiap contoh memiliki salinan dari fungsi ini.

Sekarang, bayangkan jika Anda bisa memiliki hanya satu yang get, set,delete dangetLength copy, dan setiap contoh akan referensi bahwa fungsi yang sama. Ini akan lebih baik untuk kinerja dan membutuhkan lebih sedikit memori.

Di situlah prototipe masuk. Prototipe adalah "cetak biru" dari properti yang diwarisi tetapi tidak disalin oleh instance. Jadi ini berarti bahwa ia hanya ada satu kali dalam memori untuk semua instance objek dan dibagi oleh semua instance tersebut.

Sekarang, pertimbangkan keyValueStoreobjek itu lagi. Saya bisa menulis ulang seperti ini:

var keyValueStore = (function() {
    var count = 0;
    var kvs = function() {
        count++;
        this.data = {};
    };

    kvs.prototype = {
        'get' : function(key) { return this.data[key]; },
        'set' : function(key, value) { this.data[key] = value; },
        'delete' : function(key) { delete this.data[key]; },
        'getLength' : function() {
            var l = 0;
            for (p in this.data) l++;
            return l;
        }
    };

    return  {
        'create' : function() { return new kvs(); },
        'count' : function() { return count; }
    };
})();

Ini PERSIS sama dengan versi keyValueStoreobjek sebelumnya, kecuali bahwa semua metode sekarang dimasukkan ke dalam prototipe. Apakah ini berarti, adalah bahwa semua 100 contoh sekarang berbagi empat metode ini daripada masing-masing memiliki salinan mereka sendiri.


9

Ringkasan:

  • Fungsi adalah objek dalam javascript dan dengan demikian dapat memiliki properti
  • (Konstruktor) fungsi selalu memiliki properti prototipe
  • Ketika suatu fungsi digunakan sebagai konstruktor dengan newkata kunci objek mendapat prototipe. Referensi ke prototipe ini dapat ditemukan di__proto__ properti objek yang baru dibuat.
  • __proto__Properti ini merujuk ke prototypeproperti fungsi konstruktor.

Contoh:

function Person (name) {
  this.name = name;
}

let me = new Person('willem');

console.log(Person.prototype) // Person has a prototype property

console.log(Person.prototype === me.__proto__) // the __proto__ property of the instance refers to prototype property of the function.

Mengapa ini berguna:

Javascript memiliki mekanisme ketika mencari properti pada Objects yang disebut 'prototypal inheritance' , berikut adalah apa yang pada dasarnya dilakukan:

  • Pertama diperiksa jika properti tersebut terletak di Obyek itu sendiri. Jika demikian, properti ini dikembalikan.
  • Jika properti tidak terletak pada objek itu sendiri, ia akan 'memanjat protochain'. Pada dasarnya ia melihat objek yang dirujuk oleh properti proto . Di sana ia memeriksa apakah properti tersedia pada objek yang dirujuk oleh proto
  • Jika properti tidak terletak pada objek proto, ia akan memanjat rantai proto hingga objek Object.
  • Jika tidak dapat menemukan properti di mana pun pada objek dan rantai prototipe-nya, ia akan kembali tidak terdefinisi.

Sebagai contoh:

function Person(name) {
  this.name = name;
}

let mySelf = new Person('Willem');

console.log(mySelf.__proto__ === Person.prototype);

console.log(mySelf.__proto__.__proto__ === Object.prototype);

Memperbarui:

The __proto__properti telah usang, meskipun diimplementasikan di sebagian besar browser modern cara yang lebih baik untuk mendapatkan referensi objek prototipe akan menjadi:

Object.getPrototypeOf()


7

Saya selalu menyukai analogi untuk memahami hal semacam ini. 'Warisan prototipe' cukup membingungkan dibandingkan dengan pewarisan bass kelas menurut pendapat saya, meskipun prototipe adalah paradigma yang lebih sederhana. Sebenarnya dengan prototipe, benar-benar tidak ada warisan, jadi nama itu sendiri menyesatkan, itu lebih merupakan jenis 'delegasi'.

Bayangkan ini ....

Anda berada di sekolah menengah, dan Anda berada di kelas dan memiliki kuis yang dijadwalkan hari ini, tetapi Anda tidak memiliki pena untuk mengisi jawaban Anda. Doh!

Anda duduk di sebelah teman Anda, Finnius, yang mungkin memiliki pena. Anda bertanya, dan dia melihat sekeliling mejanya dengan tidak berhasil, tetapi alih-alih mengatakan "Saya tidak punya pena", dia adalah teman baik yang dia tanyakan kepada temannya yang lain Derp jika dia punya pena. Derp memang memiliki pena cadangan dan menyerahkannya kembali ke Finnius, yang menyerahkannya kepada Anda untuk menyelesaikan kuis Anda. Derp telah mempercayakan pena itu ke Finnius, yang telah mendelegasikan pena itu untuk Anda gunakan.

Yang penting di sini adalah bahwa Derp tidak memberikan pena kepada Anda, karena Anda tidak memiliki hubungan langsung dengannya.

Ini, adalah contoh sederhana tentang cara kerja prototipe, tempat pohon data dicari untuk hal yang Anda cari.


3

skema lain yang menunjukkan __proto__ , prototipe , dan hubungan konstruktor : masukkan deskripsi gambar di sini


1

Hanya saja Anda sudah memiliki objek dengan Object.newtetapi Anda masih belum memiliki objek saat menggunakan sintaks konstruktor.


1

Sangat penting untuk memahami bahwa ada perbedaan antara prototipe objek (yang tersedia melalui Object.getPrototypeOf(obj), atau melalui __proto__properti yang sudah tidak digunakan lagi ) dan prototypeproperti pada fungsi konstruktor. Yang pertama adalah properti pada setiap contoh, dan yang terakhir adalah properti pada konstruktor. Artinya, Object.getPrototypeOf(new Foobar())mengacu pada objek yang sama dengan Foobar.prototype.

Referensi: https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Object_prototypes


0

The Prototype menciptakan objek baru dengan kloning yang ada objek . Jadi benar-benar ketika kita berpikir tentang prototipe kita benar-benar dapat berpikir kloning atau membuat salinan sesuatu daripada mengada-ada.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.