Penggunaan 'prototipe' vs. 'ini' dalam JavaScript?

776

Apa bedanya

var A = function () {
    this.x = function () {
        //do something
    };
};

dan

var A = function () { };
A.prototype.x = function () {
    //do something
};
sw234
sumber
konsep kata kunci INI dijelaskan secara eksplisit di sini scotch.io/@alZami/understanding-this-in-javascript
AL-zami
1
Membaca utas "ini" menunjukkan betapa mengerikannya JS dan seberapa banyak prinsipnya tidak jelas bagi banyak pengembang. Apa sebenarnya yang salah dengan bahasa yang lebih mudah dipahami? Saya pikir inilah saatnya pengembang mengangkat suara mereka untuk menolak teknologi membingungkan yang tidak memberikan nilai atau sedikit pun bagi pekerjaan bisnis atau pengembangan.
NoChance
Pada objek a1.x !== a2.x:; pada prototipe:a1.x === a2.x
Juan Mendes

Jawaban:

467

Contoh-contoh tersebut memiliki hasil yang sangat berbeda.

Sebelum melihat perbedaannya, hal-hal berikut harus diperhatikan:

  • Prototipe konstruktor menyediakan cara untuk berbagi metode dan nilai di antara instance melalui [[Prototype]]properti pribadi instans .
  • Fungsi ini diatur oleh bagaimana fungsi dipanggil atau oleh penggunaan bind (tidak dibahas di sini). Di mana fungsi dipanggil pada objek (misalnya myObj.method()) maka ini dalam metode referensi objek. Di mana ini tidak diatur oleh panggilan atau oleh penggunaan bind , itu default ke objek global (jendela di browser) atau dalam mode ketat, tetap tidak ditentukan.
  • JavaScript adalah bahasa berorientasi objek, yaitu sebagian besar nilai adalah objek, termasuk fungsi. (String, angka, dan boolean bukan objek.)

Jadi inilah cuplikan yang dimaksud:

var A = function () {
    this.x = function () {
        //do something
    };
};

Dalam hal ini, variabel Adiberi nilai yang merupakan referensi ke suatu fungsi. Ketika fungsi itu disebut menggunakan A(), fungsi ini tidak diatur oleh panggilan sehingga default ke objek global dan ekspresi this.xefektif window.x. Hasilnya adalah referensi ke ekspresi fungsi di sisi kanan ditugaskan window.x.

Dalam kasus:

var A = function () { };
A.prototype.x = function () {
    //do something
};

sesuatu yang sangat berbeda terjadi. Di baris pertama, variabel Aditugaskan referensi ke suatu fungsi. Dalam JavaScript, semua objek fungsi memiliki properti prototipe secara default sehingga tidak ada kode terpisah untuk membuat objek A.prototype .

Di baris kedua, A.prototype.x diberikan referensi ke suatu fungsi. Ini akan membuat properti x jika tidak ada, atau menetapkan nilai baru jika itu ada. Jadi perbedaannya dengan contoh pertama di mana properti x objek terlibat dalam ekspresi.

Contoh lain di bawah ini. Ini mirip dengan yang pertama (dan mungkin apa yang ingin Anda tanyakan):

var A = new function () {
    this.x = function () {
        //do something
    };
};

Dalam contoh ini, newoperator telah ditambahkan sebelum ekspresi fungsi sehingga fungsi tersebut disebut sebagai konstruktor. Ketika dipanggil dengan new, fungsi ini diatur untuk referensi Obyek baru yang [[Prototype]]milik pribadinya ditetapkan untuk referensi prototipe publik konstruktor . Jadi dalam pernyataan penugasan, xproperti akan dibuat pada objek baru ini. Ketika dipanggil sebagai konstruktor, fungsi mengembalikan objek ini secara default, jadi tidak perlu untuk return this;pernyataan yang terpisah .

Untuk memeriksa bahwa A memiliki properti x :

console.log(A.x) // function () {
                 //   //do something
                 // };

Ini adalah penggunaan baru yang tidak biasa karena satu-satunya cara untuk merujuk konstruktor adalah melalui A.constructor . Akan lebih umum dilakukan:

var A = function () {
    this.x = function () {
        //do something
    };
};
var a = new A();

Cara lain untuk mencapai hasil yang serupa adalah dengan menggunakan ekspresi fungsi yang langsung dipanggil:

var A = (function () {
    this.x = function () {
        //do something
    };
}());

Dalam hal ini, Atetapkan nilai balik dari memanggil fungsi di sisi kanan. Di sini sekali lagi, karena ini tidak diatur dalam panggilan, itu akan merujuk objek global dan this.xefektif window.x. Karena fungsi tidak mengembalikan apa pun, Aakan memiliki nilai undefined.

Perbedaan antara kedua pendekatan ini juga bermanifestasi jika Anda membuat cerita bersambung dan menghapus serialkan objek Javascript ke / dari JSON. Metode yang didefinisikan pada prototipe objek tidak diserialisasi ketika Anda membuat serial objek, yang mungkin nyaman ketika misalnya Anda ingin membuat serial hanya bagian data dari suatu objek, tetapi tidak metode itu:

var A = function () { 
    this.objectsOwnProperties = "are serialized";
};
A.prototype.prototypeProperties = "are NOT serialized";
var instance = new A();
console.log(instance.prototypeProperties); // "are NOT serialized"
console.log(JSON.stringify(instance)); 
// {"objectsOwnProperties":"are serialized"} 

Pertanyaan terkait :

Sidenote: Mungkin tidak ada penghematan memori yang signifikan antara kedua pendekatan, namun menggunakan prototipe untuk berbagi metode dan properti kemungkinan akan menggunakan lebih sedikit memori daripada setiap contoh memiliki salinannya sendiri.

JavaScript bukan bahasa tingkat rendah. Mungkin tidak terlalu berharga untuk memikirkan prototyping atau pola pewarisan lainnya sebagai cara untuk secara eksplisit mengubah cara memori dialokasikan.

keparo
sumber
49
@ Kemparo: Anda salah. Setiap objek memiliki objek prototipe [internal] (yang dapat berupa null), tetapi ini sangat berbeda dari prototypeproperti - yang ada di fungsi dan yang prototipe dari semua instance diatur ketika mereka dibangun dengan new. Tidak percaya ini benar-benar mendapat 87 upvotes :-(
Bergi
8
"The language is functional"apakah Anda yakin bahwa ini adalah arti fungsional?
phant0m
23
Saya kedua apa yang dikatakan @Bergi tentang prototipe. Fungsi memiliki properti prototipe. Semua objek, termasuk fungsi, memiliki properti internal lain yang dapat diakses dengan Object.getPrototypeOf (myObject) atau dengan myObject .__ proto__ di beberapa browser. The proto properti menunjukkan orangtua objek dalam rantai prototipe (atau benda dari mana obyek ini mewarisi). Properti prototipe (yang hanya pada fungsi) menunjukkan objek yang akan menjadi induk dari setiap objek yang memanfaatkan fungsi untuk membuat objek baru menggunakan kata kunci baru.
Jim Cooper
11
Artikel ini cukup sesat dan mengacaukan bagaimana ini diatur. Mengerjakan penulisan ulang.
RobG
37
Jawaban ini sangat aneh dan sepertinya sepenuhnya melewatkan inti pertanyaan. Pertanyaannya tampaknya sangat umum tentang mendefinisikan tipe properti dalam constructor vs protoype, tetapi setengah dari jawabannya adalah tentang apa yang akan terjadi jika Anda digunakan Asebagai fungsi, dan setengah lainnya adalah tentang cara-cara yang tidak jelas dan tidak lazim untuk dilakukan. sesuatu yang langsung.
JLRishe
235

Seperti yang orang lain katakan versi pertama, menggunakan "ini" menghasilkan setiap instance dari kelas A yang memiliki salinan independen sendiri dari metode fungsi "x". Sedangkan menggunakan "prototipe" akan berarti bahwa setiap instance dari kelas A akan menggunakan salinan metode "x" yang sama.

Berikut adalah beberapa kode untuk menunjukkan perbedaan halus ini:

// x is a method assigned to the object using "this"
var A = function () {
    this.x = function () { alert('A'); };
};
A.prototype.updateX = function( value ) {
    this.x = function() { alert( value ); }
};

var a1 = new A();
var a2 = new A();
a1.x();  // Displays 'A'
a2.x();  // Also displays 'A'
a1.updateX('Z');
a1.x();  // Displays 'Z'
a2.x();  // Still displays 'A'

// Here x is a method assigned to the object using "prototype"
var B = function () { };
B.prototype.x = function () { alert('B'); };

B.prototype.updateX = function( value ) {
    B.prototype.x = function() { alert( value ); }
}

var b1 = new B();
var b2 = new B();
b1.x();  // Displays 'B'
b2.x();  // Also displays 'B'
b1.updateX('Y');
b1.x();  // Displays 'Y'
b2.x();  // Also displays 'Y' because by using prototype we have changed it for all instances

Seperti yang telah disebutkan orang lain, ada berbagai alasan untuk memilih satu metode atau yang lain. Sampel saya hanya dimaksudkan untuk menunjukkan perbedaan secara jelas.

Benry
sumber
5
Inilah yang saya harapkan akan terjadi, tetapi ketika saya membuat instance objek baru setelah mengubah Axe seperti di atas, saya masih menampilkan 'A' kecuali saya menggunakan A seperti singleton. jsbin.com/omida4/2/edit
jellyfishtree
19
Itu karena contoh saya salah. Itu hanya salah selama dua tahun. Mendesah. Namun intinya masih berlaku. Saya memperbarui contoh dengan yang benar-benar berfungsi. Terima kasih telah menunjukkannya.
Benry
4
Ini metode statis! : D
6
ya ... 'prototipe' berarti level statis atau kelas .. yang akan dibagikan oleh semua instance yang dibuat ... sementara 'ini' adalah metode instance yang setiap instance akan memiliki salinannya sendiri
Aneer Dev
7
Itu tidak statis. Statis, seperti yang digunakan dalam kebanyakan bahasa OO, menyiratkan bahwa tidak ada ketergantungan pada thisobjek, yang merupakan pemilik metode. yaitu metode tidak memiliki objek yang merupakan pemiliknya. Dalam hal ini ada thisobjek, seperti yang ditunjukkan di kelas A dalam contoh.
CJStuart
152

Ambil 2 contoh ini:

var A = function() { this.hey = function() { alert('from A') } };

vs.

var A = function() {}
A.prototype.hey = function() { alert('from prototype') };

Kebanyakan orang di sini (terutama jawaban berperingkat teratas) mencoba menjelaskan bagaimana mereka berbeda tanpa menjelaskan MENGAPA. Saya pikir ini salah dan jika Anda memahami dasar-dasarnya terlebih dahulu, perbedaannya akan menjadi jelas. Mari kita coba jelaskan dasarnya dulu ...

a) Fungsi adalah objek dalam JavaScript. SETIAP objek dalam JavaScript mendapat properti internal (artinya, Anda tidak dapat mengaksesnya seperti properti lain, kecuali mungkin di browser seperti Chrome), sering disebut sebagai __proto__(Anda sebenarnya dapat mengetik anyObject.__proto__di Chrome untuk melihat apa yang dirujuk. Ini hanya itu , properti, tidak lebih. Properti dalam JavaScript = variabel di dalam objek, tidak lebih. Apa yang dilakukan variabel? Mereka menunjukkan sesuatu.

Jadi apa yang __proto__ditunjukkan properti ini ? Ya, biasanya objek lain (kami akan jelaskan alasannya nanti). Satu-satunya cara untuk memaksa JavaScript agar __proto__properti TIDAK menunjuk ke objek lain adalah dengan menggunakannya var newObj = Object.create(null). Bahkan jika Anda melakukan ini, __proto__properti MASIH ada sebagai properti objek, hanya saja tidak menunjuk ke objek lain, itu menunjuk ke null.

Di sinilah kebanyakan orang menjadi bingung:

Ketika Anda membuat fungsi baru dalam JavaScript (yang juga merupakan objek, ingat?), Saat itu didefinisikan, JavaScript secara otomatis membuat properti baru pada fungsi yang disebut prototype. Cobalah:

var A = [];
A.prototype // undefined
A = function() {}
A.prototype // {} // got created when function() {} was defined

A.prototype BENAR - BENAR BERBEDA dari __proto__ properti. Dalam contoh kami, 'A' sekarang memiliki DUA properti yang disebut 'prototipe' dan __proto__. Ini adalah kebingungan besar bagi orang-orang. prototypedan __proto__properti sama sekali tidak terkait, mereka hal-hal yang terpisah yang menunjuk ke nilai yang terpisah.

Anda mungkin bertanya-tanya: Mengapa JavaScript memiliki __proto__properti yang dibuat pada setiap objek? Satu kata: delegasi . Ketika Anda memanggil properti pada objek dan objek tidak memilikinya, maka JavaScript mencari objek yang direferensikan __proto__untuk melihat apakah mungkin memilikinya. Jika tidak memilikinya, maka terlihat pada __proto__properti objek itu dan seterusnya ... hingga rantai berakhir. Demikianlah nama rantai prototipe . Tentu saja jika__proto__ tidak menunjuk ke objek dan malah menunjuk ke null, semoga beruntung, JavaScript menyadari itu dan akan mengembalikan Anda undefineduntuk properti.

Anda mungkin juga bertanya-tanya, mengapa JavaScript membuat properti yang dipanggil prototypeuntuk suatu fungsi ketika Anda mendefinisikan fungsinya? Karena itu mencoba membodohi Anda, ya menipu Anda bahwa itu bekerja seperti bahasa berbasis kelas.

Mari kita lanjutkan dengan contoh kita dan membuat "objek" dari A:

var a1 = new A();

Ada sesuatu yang terjadi di latar belakang ketika hal ini terjadi. a1adalah variabel biasa yang diberi objek kosong baru.

Fakta bahwa Anda menggunakan operator newsebelum pemanggilan fungsi A()melakukan sesuatu TAMBAHAN di latar belakang. Kata newkunci membuat objek baru yang sekarang referensi a1dan objek itu kosong. Inilah yang terjadi sebagai tambahan:

Kami mengatakan bahwa pada setiap definisi fungsi ada properti baru yang dibuat bernama prototype (yang dapat Anda akses, tidak seperti dengan __proto__properti) yang dibuat? Nah, properti itu sedang digunakan sekarang.

Jadi kita sekarang pada titik di mana kita memiliki a1objek kosong yang baru dipanggang . Kami mengatakan bahwa semua objek dalam JavaScript memiliki __proto__properti internal yang menunjuk ke sesuatu ( a1juga memilikinya), apakah itu null atau objek lain. Apa yang dilakukan oleh newoperator adalah menetapkan __proto__properti itu untuk menunjuk ke prototypeproperti fungsi . Baca lagi. Ini pada dasarnya ini:

a1.__proto__ = A.prototype;

Kami mengatakan bahwa A.prototypetidak lebih dari objek kosong (kecuali kami mengubahnya ke sesuatu yang lain sebelum mendefinisikan a1). Jadi sekarang pada dasarnya a1.__proto__menunjuk ke hal yang sama A.prototypemenunjuk ke, yaitu objek kosong itu. Keduanya menunjuk ke objek yang sama yang dibuat ketika baris ini terjadi:

A = function() {} // JS: cool. let's also create A.prototype pointing to empty {}

Sekarang, ada hal lain yang terjadi ketika var a1 = new A()pernyataan diproses. Pada dasarnya A()dieksekusi dan jika A adalah sesuatu seperti ini:

var A = function() { this.hey = function() { alert('from A') } };

Semua hal di dalam function() { }akan dieksekusi. Ketika Anda mencapai this.hey..garis, thisdiubah ke a1dan Anda mendapatkan ini:

a1.hey = function() { alert('from A') }

Saya tidak akan membahas mengapa thisperubahan a1tetapi ini adalah jawaban yang bagus untuk mempelajari lebih lanjut.

Jadi untuk meringkas, ketika Anda melakukannya var a1 = new A() ada 3 hal yang terjadi di latar belakang:

  1. Objek kosong yang sama sekali baru dibuat dan ditugaskan a1.a1 = {}
  2. a1.__proto__properti ditugaskan untuk menunjuk pada hal yang sama dengan A.prototypemenunjuk ke (objek kosong lain {})

  3. Fungsi A()ini dieksekusi dengan thisset ke objek kosong baru yang dibuat pada langkah 1 (baca jawaban yang saya rujuk di atas tentang mengapa thisperubahan a1)

Sekarang, mari kita coba membuat objek lain:

var a2 = new A();

Langkah 1,2,3 akan diulang. Apakah Anda memperhatikan sesuatu? Kata kuncinya adalah repeat. Langkah 1: a2akan menjadi objek kosong baru, langkah 2: __proto__propertinya akan menunjuk ke hal yang sama A.prototypedan yang paling penting, langkah 3: fungsi A()LAGI dieksekusi, yang berarti bahwa a2akan mendapatkan heyproperti yang mengandung fungsi. a1dan a2memiliki dua properti SEPARATE bernama heyyang mengarah ke 2 fungsi SEPARATE! Kami sekarang memiliki fungsi duplikat dalam dua objek yang berbeda melakukan hal yang sama, oops ... Anda dapat membayangkan implikasi memori ini jika kami memiliki 1000 objek yang dibuat dengannew A , setelah semua fungsi deklarasi mengambil lebih banyak memori daripada sesuatu seperti angka 2. Jadi bagaimana kita mencegah ini?

Ingat mengapa __proto__properti ada di setiap objek? Sehingga jika Anda mengambil yoManproperti pada a1(yang tidak ada), __proto__propertinya akan dikonsultasikan, yang jika itu adalah objek (dan sebagian besar kasusnya adalah), ia akan memeriksa apakah itu berisi yoMan, dan jika tidak, itu akan berkonsultasi dengan objek itu __proto__dll. Jika ya, ia akan mengambil nilai properti itu dan menampilkannya kepada Anda.

Jadi seseorang memutuskan untuk menggunakan fakta ini + fakta bahwa ketika Anda membuat a1, __proto__propertinya menunjuk ke objek yang sama (kosong) A.prototypemenunjuk ke dan melakukan ini:

var A = function() {}
A.prototype.hey = function() { alert('from prototype') };

Keren! Sekarang, ketika Anda membuat a1, itu lagi melewati semua 3 langkah di atas, dan pada langkah 3, itu tidak melakukan apa-apa, karena function A()tidak ada yang dieksekusi. Dan jika kita melakukannya:

a1.hey

Ini akan melihat yang a1tidak mengandung heydan akan memeriksa nya__proto__ objek propertinya untuk melihat apakah memilikinya, yang merupakan kasusnya.

Dengan pendekatan ini kami menghilangkan bagian dari langkah 3 di mana fungsi diduplikasi pada setiap pembuatan objek baru. Alih-alih a1dan a2memiliki heyproperti terpisah , sekarang NONE dari mereka memilikinya. Yang, kurasa, kau sudah tahu sendiri sekarang. Itu hal yang baik ... jika Anda mengerti __proto__dan Function.prototype, pertanyaan seperti ini akan sangat jelas.

CATATAN: Beberapa orang cenderung tidak menyebut properti Prototipe internal sebagai __proto__, Saya telah menggunakan nama ini melalui pos untuk membedakannya dengan jelas dengan Functional.prototypeproperti sebagai dua hal yang berbeda.

daremkd
sumber
1
Jawaban yang benar-benar menyeluruh dan informatif. Saya melakukan beberapa tes memori menggunakan struktur objek di atas (A.prototype.hey vs objek this.hey) dan menciptakan 1000 contoh masing-masing. Jejak memori untuk pendekatan properti objek sekitar 100kb lebih besar dibandingkan dengan prototipe. Saya kemudian menambahkan fungsi lain dengan tujuan yang sama yang disebut "konyol" dan meningkat secara linear menjadi 200kb. Tidak signifikan, tetapi juga bukan kacang.
jookyone
Yang lebih menarik adalah bahwa metode prototipe sedikit lebih lambat daripada metode properti objek yang berjalan secara lokal. Secara keseluruhan, saya tidak yakin bahwa javascript harus digunakan untuk manipulasi data objek yang berjumlah di atas 10k, oleh karena itu meniadakan alasan untuk mengubah pendekatan berdasarkan efek memori potensial. Pada saat itu pekerjaan tersebut harus diturunkan ke server.
jookyone
Intinya adalah __proto__dan .prototypeadalah hal yang sangat berbeda.
Wayou
1
Saya tidak merasa puas hanya memberi Anda upvote ... Bagus sekali!
Kristianmitk
58

Dalam kebanyakan kasus mereka pada dasarnya sama, tetapi versi kedua menyimpan memori karena hanya ada satu contoh fungsi alih-alih fungsi terpisah untuk setiap objek.

Alasan untuk menggunakan formulir pertama adalah untuk mengakses "anggota pribadi". Sebagai contoh:

var A = function () {
    var private_var = ...;

    this.x = function () {
        return private_var;
    };

    this.setX = function (new_x) {
        private_var = new_x;
    };
};

Karena aturan pelingkupan javascript, private_var tersedia untuk fungsi yang ditetapkan untuk this.x, tetapi tidak di luar objek.

Matthew Crumley
sumber
1
Lihat posting ini: stackoverflow.com/a/1441692/654708 untuk contoh tentang cara mengakses anggota pribadi melalui prototipe.
GFoley83
@ GFoley83 jawaban itu tidak menunjukkan bahwa - metode prototipe hanya dapat mengakses properti "publik" dari objek yang diberikan. Hanya metode istimewa (bukan pada prototipe) yang dapat mengakses anggota pribadi.
Alnitak
27

Contoh pertama mengubah antarmuka untuk objek itu saja. Contoh kedua mengubah antarmuka untuk semua objek dari kelas itu.

Glenn
sumber
Keduanya akan membuat fungsi xtersedia untuk semua objek yang prototipenya diberikan contoh baru dari A:function B () {}; B.prototype = new A(); var b = new B(); b.x() // Will call A.x if A is defined by first example;
Spencer Williams
21

Masalah utama dengan menggunakan thisalih-alih prototypeadalah ketika menimpa metode, konstruktor dari kelas dasar masih akan merujuk pada metode yang diganti. Pertimbangkan ini:

BaseClass = function() {
    var text = null;

    this.setText = function(value) {
        text = value + " BaseClass!";
    };

    this.getText = function() {
        return text;
    };

    this.setText("Hello"); // This always calls BaseClass.setText()
};

SubClass = function() {
    // setText is not overridden yet,
    // so the constructor calls the superclass' method
    BaseClass.call(this);

    // Keeping a reference to the superclass' method
    var super_setText = this.setText;
    // Overriding
    this.setText = function(value) {
        super_setText.call(this, "SubClass says: " + value);
    };
};
SubClass.prototype = new BaseClass();

var subClass = new SubClass();
console.log(subClass.getText()); // Hello BaseClass!

subClass.setText("Hello"); // setText is already overridden
console.log(subClass.getText()); // SubClass says: Hello BaseClass!

melawan:

BaseClass = function() {
    this.setText("Hello"); // This calls the overridden method
};

BaseClass.prototype.setText = function(value) {
    this.text = value + " BaseClass!";
};

BaseClass.prototype.getText = function() {
    return this.text;
};

SubClass = function() {
    // setText is already overridden, so this works as expected
    BaseClass.call(this);
};
SubClass.prototype = new BaseClass();

SubClass.prototype.setText = function(value) {
    BaseClass.prototype.setText.call(this, "SubClass says: " + value);
};

var subClass = new SubClass();
console.log(subClass.getText()); // SubClass says: Hello BaseClass!

Jika Anda pikir ini bukan masalah, maka itu tergantung pada apakah Anda bisa hidup tanpa variabel pribadi, dan apakah Anda cukup berpengalaman untuk mengetahui kebocoran saat Anda melihatnya. Juga, harus meletakkan logika konstruktor setelah definisi metode tidak nyaman.

var A = function (param1) {
    var privateVar = null; // Private variable

    // Calling this.setPrivateVar(param1) here would be an error

    this.setPrivateVar = function (value) {
        privateVar = value;
        console.log("setPrivateVar value set to: " + value);

        // param1 is still here, possible memory leak
        console.log("setPrivateVar has param1: " + param1);
    };

    // The constructor logic starts here possibly after
    // many lines of code that define methods

    this.setPrivateVar(param1); // This is valid
};

var a = new A(0);
// setPrivateVar value set to: 0
// setPrivateVar has param1: 0

a.setPrivateVar(1);
//setPrivateVar value set to: 1
//setPrivateVar has param1: 0

melawan:

var A = function (param1) {
    this.setPublicVar(param1); // This is valid
};
A.prototype.setPublicVar = function (value) {
    this.publicVar = value; // No private variable
};

var a = new A(0);
a.setPublicVar(1);
console.log(a.publicVar); // 1
tarkabak
sumber
20

Setiap objek terkait dengan objek prototipe. Saat mencoba mengakses properti yang tidak ada, JavaScript akan mencari objek prototipe objek untuk properti itu dan mengembalikannya jika ada.

The prototypemilik konstruktor fungsi mengacu pada objek prototipe dari semua contoh dibuat dengan fungsi yang ketika menggunakan new.


Dalam contoh pertama Anda, Anda menambahkan properti xke setiap instance yang dibuat dengan Afungsi.

var A = function () {
    this.x = function () {
        //do something
    };
};

var a = new A();    // constructor function gets executed
                    // newly created object gets an 'x' property
                    // which is a function
a.x();              // and can be called like this

Dalam contoh kedua Anda menambahkan properti ke objek prototipe yang dibuat semua Atitik dengan menunjuk.

var A = function () { };
A.prototype.x = function () {
    //do something
};

var a = new A();    // constructor function gets executed
                    // which does nothing in this example

a.x();              // you are trying to access the 'x' property of an instance of 'A'
                    // which does not exist
                    // so JavaScript looks for that property in the prototype object
                    // that was defined using the 'prototype' property of the constructor

Sebagai kesimpulan, dalam contoh pertama salinan fungsi ditugaskan untuk setiap contoh . Dalam contoh kedua, satu salinan fungsi dibagikan oleh semua instance .

pishpish
sumber
1
Memilih ini sebagai jawaban paling langsung untuk pertanyaan itu.
Nick Pineda
1
Saya menyukai pendekatan langsung Anda !! berdebar!
Pangeran Vijay Pratap
16

Apa bedanya? => Banyak.

Saya pikir, thisversi ini digunakan untuk mengaktifkan enkapsulasi, yaitu penyembunyian data. Ini membantu untuk memanipulasi variabel pribadi.

Mari kita lihat contoh berikut:

var AdultPerson = function() {

  var age;

  this.setAge = function(val) {
    // some housekeeping
    age = val >= 18 && val;
  };

  this.getAge = function() {
    return age;
  };

  this.isValid = function() {
    return !!age;
  };
};

Sekarang, prototypestruktur dapat diterapkan sebagai berikut:

Orang dewasa yang berbeda memiliki usia yang berbeda, tetapi semua orang dewasa mendapatkan hak yang sama.
Jadi, kami menambahkannya menggunakan prototipe, bukan ini.

AdultPerson.prototype.getRights = function() {
  // Should be valid
  return this.isValid() && ['Booze', 'Drive'];
};

Mari kita lihat implementasinya sekarang.

var p1 = new AdultPerson;
p1.setAge(12); // ( age = false )
console.log(p1.getRights()); // false ( Kid alert! )
p1.setAge(19); // ( age = 19 )
console.log(p1.getRights()); // ['Booze', 'Drive'] ( Welcome AdultPerson )

var p2 = new AdultPerson;
p2.setAge(45);    
console.log(p2.getRights()); // The same getRights() method, *** not a new copy of it ***

Semoga ini membantu.

oozzal
sumber
3
+1 Jawaban yang jauh lebih berbelit-belit dan lebih grafis daripada yang lain. Tetapi Anda harus menguraikan lebih banyak sebelum memberikan contoh-contoh (baik) ini.
yerforkferchips
1
Saya tidak yakin tentang "versi ini digunakan untuk mengaktifkan enkapsulasi, yaitu penyembunyian data". Jika properti di dalam suatu fungsi didefinisikan menggunakan "ini" seperti dalam "this.myProperty = ...", properti seperti itu bukan "pribadi" dan dapat diakses dari objek di luar kelas menggunakan "baru".
NoChance
14

Prototipe adalah templat kelas; yang berlaku untuk semua contoh masa depan itu. Padahal ini adalah contoh khusus dari objek.

Harropriiz
sumber
14

Saya tahu ini telah dijawab sampai mati tetapi saya ingin menunjukkan contoh nyata perbedaan kecepatan.

Berfungsi langsung pada objek

Berfungsi pada prototipe

Di sini kami membuat 2.000.000 objek baru dengan printmetode di Chrome. Kami menyimpan setiap objek dalam array. Memakai printprototipe membutuhkan sekitar 1/2 selama.

Arnav Aggarwal
sumber
13

Biarkan saya memberi Anda jawaban yang lebih komprehensif yang saya pelajari selama kursus pelatihan JavaScript.

Sebagian besar jawaban menyebutkan perbedaannya, yaitu ketika prototyping fungsi dibagi dengan semua instance (masa depan). Sedangkan mendeklarasikan fungsi di kelas akan membuat salinan untuk setiap instance.

Secara umum tidak ada benar atau salah, ini lebih merupakan masalah selera atau keputusan desain tergantung pada kebutuhan Anda. Namun prototipe adalah teknik yang digunakan untuk mengembangkan dengan cara yang berorientasi objek, karena saya harap Anda akan melihat pada akhir jawaban ini.

Anda menunjukkan dua pola dalam pertanyaan Anda. Saya akan mencoba menjelaskan dua lagi dan mencoba menjelaskan perbedaannya jika relevan. Jangan ragu untuk mengedit / memperluas. Dalam semua contoh ini tentang objek mobil yang memiliki lokasi dan dapat bergerak.

Pola Penghias Objek

Tidak yakin apakah pola ini masih relevan saat ini, tetapi ada. Dan baik untuk mengetahuinya. Anda cukup meneruskan objek dan properti ke fungsi dekorator. Penghias mengembalikan objek dengan properti dan metode.

var carlike = function(obj, loc) {
    obj.loc = loc;
    obj.move = function() {
        obj.loc++;
    };
    return obj;
};

var amy = carlike({}, 1);
amy.move();
var ben = carlike({}, 9);
ben.move();

Kelas Fungsional

Fungsi dalam JavaScript adalah objek khusus. Selain dipanggil, fungsi dapat menyimpan properti seperti objek lainnya.

Dalam hal ini Caradalah fungsi ( juga pikirkan objek ) yang dapat dipanggil seperti yang biasa Anda lakukan. Ia memiliki properti methods(yang merupakan objek dengan movefungsi). Ketika Cardipanggil extendfungsi dipanggil, yang melakukan beberapa sihir, dan memperluas Carfungsi (pikirkan objek) dengan metode yang didefinisikan dalam methods.

Contoh ini, meskipun berbeda, paling mendekati contoh pertama dalam pertanyaan.

var Car = function(loc) {
    var obj = {loc: loc};
    extend(obj, Car.methods);
    return obj;
};

Car.methods = {
    move : function() {
        this.loc++;
    }
};

var amy = Car(1);
amy.move();
var ben = Car(9);
ben.move();

Kelas Prototypal

Dua pola pertama memungkinkan diskusi tentang menggunakan teknik untuk menentukan metode bersama atau menggunakan metode yang didefinisikan inline di tubuh konstruktor. Dalam kedua kasus setiap instance memiliki fungsinya sendiri move.

Pola prototypal tidak cocok dengan ujian yang sama, karena pembagian fungsi melalui delegasi prototipe adalah tujuan utama untuk pola prototypal. Seperti yang ditunjukkan orang lain, diharapkan memiliki jejak memori yang lebih baik.

Namun ada satu hal yang menarik untuk diketahui: Setiap prototypeobjek memiliki properti kenyamanan constructor, yang menunjuk kembali ke fungsi (pikir objek) yang melekat padanya.

Mengenai tiga baris terakhir:

Dalam contoh ini Carlink ke prototypeobjek, yang menghubungkan melalui constructoruntuk Cardirinya sendiri, yaitu Car.prototype.constructoradalahCar dirinya sendiri. Ini memungkinkan Anda untuk mengetahui fungsi konstruktor mana yang membangun objek tertentu.

amy.constructorPencarian gagal dan dengan demikian didelegasikan kepada Car.prototype, yang memang memiliki properti konstruktor. Begitu amy.constructorjuga Car.

Selanjutnya, amyadalah instanceof Car. The instanceofOperator bekerja dengan melihat jika operan kanan ini objek prototipe ( Car) dapat ditemukan di mana saja di prototipe operan kiri ini ( amy) rantai.

var Car = function(loc) {
    var obj = Object.create(Car.prototype);
    obj.loc = loc;
    return obj;
};

Car.prototype.move = function() {
        this.loc++;
};

var amy = Car(1);
amy.move();
var ben = Car(9);
ben.move();

console.log(Car.prototype.constructor);
console.log(amy.constructor);
console.log(amy instanceof Car);

Beberapa pengembang mungkin bingung pada awalnya. Lihat contoh di bawah ini:

var Dog = function() {
  return {legs: 4, bark: alert};
};

var fido = Dog();
console.log(fido instanceof Dog);

The instanceofOperator kembali false, karena Dog's prototipe tidak dapat ditemukan di mana saja di fido' s rantai prototipe. fidoadalah objek sederhana yang dibuat dengan objek literal, yaitu hanya didelegasikan kepadaObject.prototype .

Pola pseudoklasik

Ini benar-benar hanyalah bentuk lain dari pola prototypal dalam bentuk yang disederhanakan dan lebih akrab untuk dilakukan orang-orang yang memprogram di Jawa misalnya, karena menggunakan newkonstruktor.

Ia melakukan hal yang sama seperti pada pola prototypal, itu hanya sintaksis gula yang melampaui pola prototypal.

Namun, perbedaan utama adalah bahwa ada optimasi diimplementasikan dalam mesin JavaScript yang hanya berlaku ketika menggunakan pola pseudoklasik. Pikirkan pola pseudoklasik versi yang mungkin lebih cepat dari pola prototypal; hubungan objek dalam kedua contoh adalah sama.

var Car = function(loc) {
    this.loc = loc;
};

Car.prototype.move = function() {
        this.loc++;
};

var amy = new Car(1);
amy.move();
var ben = new Car(9);
ben.move();

Akhirnya, seharusnya tidak terlalu sulit untuk menyadari bagaimana pemrograman berorientasi objek dapat dilakukan. Ada dua bagian.

Satu bagian yang mendefinisikan properti / metode umum dalam prototipe (rantai).

Dan bagian lain di mana Anda meletakkan definisi yang membedakan objek satu sama lain ( locvariabel dalam contoh).

Inilah yang memungkinkan kami untuk menerapkan konsep seperti superclass atau subclass dalam JavaScript.

Jangan ragu untuk menambah atau mengedit. Sekali lagi lengkap saya bisa membuat ini menjadi komunitas wiki.

Ely
sumber
Bukan untuk mengetuk posting yang sangat teliti, tetapi saya pikir OO dan pewarisan Prototypical pada dasarnya adalah aliran pemikiran yang berbeda.
Nick Pineda
Ya, tapi orang bisa "melakukan OO" dengan teknik / pemikiran yang berbeda, bukan?
Ely
Tidak yakin juga. Banyak yang hanya mengatakan bahwa filosofi prototipikal hanya berbeda dan banyak yang mencoba membandingkannya dengan OO karena itu adalah aliran pemikiran yang digunakan banyak orang.
Nick Pineda
Maksud saya, jika Anda ingin mempraktikkan gaya OO dan bahasanya menawarkan serangkaian teknik yang membantu melakukannya, itu tidak selalu salah.
Ely
11

Saya percaya bahwa @Matthew Crumley benar. Mereka secara fungsional , jika tidak secara struktural, setara. Jika Anda menggunakan Firebug untuk melihat objek yang dibuat menggunakan new, Anda dapat melihat bahwa mereka sama. Namun, preferensi saya adalah sebagai berikut. Saya menduga bahwa sepertinya lebih seperti apa yang saya terbiasa di C # / Java. Yaitu, definisikan kelas, tentukan bidang, konstruktor, dan metode.

var A = function() {};
A.prototype = {
    _instance_var: 0,

    initialize: function(v) { this._instance_var = v; },

    x: function() {  alert(this._instance_var); }
};

EDIT Tidak bermaksud menyiratkan bahwa ruang lingkup variabel adalah pribadi, saya hanya mencoba menggambarkan bagaimana saya mendefinisikan kelas saya di javascript. Nama variabel telah diubah untuk mencerminkan ini.

tvanfosson
sumber
2
_instance_var seperti pada properti initializedan x methods do not refer to the _instance_var` pada Ainstance, tetapi ke properti global. Gunakan this._instance_varjika Anda bermaksud menggunakan _instance_varproperti Ainstance.
Lekensteyn
2
Yang lucu adalah, Benry juga membuat kesalahan seperti itu, yang telah terungkap setelah dua tahun juga: p
Lekensteyn
10

Seperti dibahas dalam jawaban lain, itu benar-benar pertimbangan kinerja karena fungsi dalam prototipe dibagi dengan semua instantiations - daripada fungsi yang dibuat untuk setiap instantiation.

Saya mengumpulkan jsperf untuk menunjukkan ini. Ada perbedaan dramatis dalam waktu yang diperlukan untuk membuat instance kelas, meskipun itu benar-benar hanya relevan jika Anda membuat banyak instance.

http://jsperf.com/functions-in-constructor-vs-prototype

Devgr
sumber
8

Pikirkan tentang bahasa yang diketik secara statis, hal-hal pada prototypestatis dan hal this-hal terkait misalnya.

Wayou
sumber