Jawaban:
Perbedaan dasarnya adalah bahwa fungsi konstruktor digunakan dengan newkata kunci (yang menyebabkan JavaScript secara otomatis membuat objek baru, mengatur thisdalam fungsi ke objek itu, dan mengembalikan objek):
var objFromConstructor = new ConstructorFunction();
Fungsi pabrik disebut seperti fungsi "biasa":
var objFromFactory = factoryFunction();
Tetapi untuk itu dianggap sebagai "pabrik" itu perlu mengembalikan contoh baru dari beberapa objek: Anda tidak akan menyebutnya sebagai "pabrik" fungsi jika itu hanya mengembalikan boolean atau sesuatu. Ini tidak terjadi secara otomatis seperti dengan new, tetapi memang memungkinkan lebih banyak fleksibilitas untuk beberapa kasus.
Dalam contoh yang sangat sederhana, fungsi yang dirujuk di atas mungkin terlihat seperti ini:
function ConstructorFunction() {
this.someProp1 = "1";
this.someProp2 = "2";
}
ConstructorFunction.prototype.someMethod = function() { /* whatever */ };
function factoryFunction() {
var obj = {
someProp1 : "1",
someProp2 : "2",
someMethod: function() { /* whatever */ }
};
// other code to manipulate obj in some way here
return obj;
}
Tentu saja Anda dapat membuat fungsi pabrik jauh lebih rumit daripada contoh sederhana itu.
Salah satu keuntungan fungsi pabrik adalah ketika objek yang akan dikembalikan bisa dari beberapa jenis berbeda tergantung pada beberapa parameter.
someMethoduntuk objek yang dikembalikan oleh pabrik, dan di sanalah ia menjadi sedikit berkabut. Di dalam fungsi pabrik, jika ada begitu saja var obj = { ... , someMethod: function() {}, ... }, itu akan menyebabkan setiap objek kembali memegang salinan berbeda someMethodyang merupakan sesuatu yang mungkin tidak kita inginkan. Di situlah menggunakan newdan prototypedi dalam fungsi pabrik akan membantu.
newfungsi konstruktor; Saya pikir di situlah orang mungkin perlu melihat cara mengganti konstruktor dengan contoh fungsi pabrik dan di situlah saya pikir konsistensi dalam contoh diperlukan. Bagaimanapun, jawabannya cukup informatif. Ini hanya poin yang ingin saya sampaikan, bukan karena saya menurunkan kualitas jawaban dengan cara apa pun.
newinternal, atau menggunakan Object.create()untuk membuat objek dengan prototipe tertentu.
Sebagian besar buku mengajarkan Anda untuk menggunakan konstruktor dan new
this merujuk ke objek baru
Beberapa orang suka cara var myFoo = new Foo();membaca.
Detail Instansiasi bocor ke API panggilan (melalui newpersyaratan), sehingga semua penelepon sangat erat dengan implementasi konstruktor. Jika Anda membutuhkan fleksibilitas tambahan dari pabrik, Anda harus memperbaiki semua penelepon (tentu saja kasus yang luar biasa, bukan aturan).
Lupa newadalah bug yang umum, Anda harus sangat mempertimbangkan menambahkan cek boilerplate untuk memastikan bahwa konstruktor dipanggil dengan benar ( if (!(this instanceof Foo)) { return new Foo() }). EDIT: Sejak ES6 (ES2015) Anda tidak bisa melupakan newdengan classkonstruktor, atau konstruktor akan melempar kesalahan.
Jika Anda melakukan instanceofpemeriksaan, itu meninggalkan ambiguitas apakah newdiperlukan atau tidak . Menurut saya, seharusnya tidak. Anda secara efektif menghubung pendek newpersyaratan, yang berarti Anda dapat menghapus kekurangan # 1. Tapi kemudian Anda baru saja mendapatkan fungsi pabrik di semua kecuali nama , dengan pelat tambahan, huruf kapital, dan thiskonteks yang kurang fleksibel .
Tetapi perhatian utama saya adalah bahwa itu melanggar prinsip terbuka / tertutup. Anda mulai mengekspor konstruktor, pengguna mulai menggunakan konstruktor, kemudian di ujung jalan Anda menyadari bahwa Anda memerlukan fleksibilitas pabrik, sebagai gantinya (misalnya, untuk mengubah implementasi untuk menggunakan kumpulan objek, atau untuk instantiate di seluruh konteks eksekusi, atau untuk memiliki fleksibilitas pewarisan lebih banyak menggunakan O prototypal).
Kamu terjebak. Anda tidak dapat melakukan perubahan tanpa melanggar semua kode yang memanggil konstruktor Anda new. Anda tidak dapat beralih menggunakan kumpulan objek untuk keuntungan kinerja, misalnya.
Selain itu, menggunakan konstruktor memberi Anda tipuan instanceofyang tidak berfungsi di seluruh konteks eksekusi, dan tidak berfungsi jika prototipe konstruktor Anda diganti. Ini juga akan gagal jika Anda mulai kembali thisdari konstruktor Anda, dan kemudian beralih ke mengekspor objek sewenang-wenang, yang harus Anda lakukan untuk mengaktifkan perilaku seperti pabrik di konstruktor Anda.
Lebih sedikit kode - tidak perlu boilerplate.
Anda dapat mengembalikan objek arbitrer apa pun, dan menggunakan prototipe arbitrer apa pun - memberi Anda lebih banyak fleksibilitas untuk membuat berbagai jenis objek yang mengimplementasikan API yang sama. Misalnya, pemutar media yang dapat membuat instance HTML5 dan flash player, atau pustaka acara yang dapat memancarkan acara DOM atau acara soket web. Pabrik juga dapat membuat instance objek di seluruh konteks eksekusi, mengambil keuntungan dari kumpulan objek, dan memungkinkan model pewarisan prototypal yang lebih fleksibel.
Anda tidak akan pernah perlu mengubah dari pabrik ke konstruktor, jadi refactoring tidak akan pernah menjadi masalah.
Tidak ada ambiguitas tentang penggunaan new. Jangan. (Ini akan membuat thisberperilaku buruk, lihat poin berikutnya).
thisberperilaku seperti biasa - sehingga Anda dapat menggunakannya untuk mengakses objek induk (misalnya, di dalam player.create(), thismengacu pada player, sama seperti metode doa lainnya akan, calldan applyjuga menugaskan kembali this, seperti yang diharapkan. Jika Anda menyimpan prototipe pada objek induk, itu bisa menjadi cara yang bagus untuk menukar fungsionalitas secara dinamis, dan memungkinkan polimorfisme yang sangat fleksibel untuk instantiasi objek Anda.
Tidak ada ambiguitas tentang apakah akan memanfaatkan atau tidak. Jangan. Alat serat akan mengeluh, dan kemudian Anda akan tergoda untuk mencoba menggunakannya new, dan kemudian Anda akan membatalkan manfaat yang dijelaskan di atas.
Beberapa orang suka caranya var myFoo = foo();atau var myFoo = foo.create();membaca.
newtidak berperilaku seperti yang diharapkan (lihat di atas). Solusi: jangan gunakan itu.
thistidak merujuk ke objek baru (sebagai gantinya, jika konstruktor dipanggil dengan notasi titik atau notasi braket persegi, misalnya foo.bar () - thismengacu pada foo- sama seperti setiap metode JavaScript lainnya - lihat manfaatnya).
newmelanggar prinsip terbuka / tertutup. Lihat medium.com/javascript-scene/… untuk diskusi yang jauh lebih besar daripada komentar ini.
newkata kunci, saya tidak percaya bahwa newkata kunci sebenarnya memberikan keterbacaan tambahan. IMO, rasanya konyol untuk melompat melalui lingkaran untuk memungkinkan penelepon mengetik lebih banyak.
Konstruktor mengembalikan sebuah instance dari kelas tempat Anda menyebutnya. Fungsi pabrik dapat mengembalikan apa pun. Anda akan menggunakan fungsi pabrik saat Anda harus mengembalikan nilai arbitrer atau ketika sebuah kelas memiliki proses pengaturan yang besar.
function User(name) {
this.name = name;
this.isAdmin = false;
}
let user = new User("Jack");
newmembuat objek yang di-prototipe User.prototypedan memanggil Userdengan objek yang dibuat sebagai thisnilainya.
new memperlakukan ekspresi argumen untuk operan sebagai opsional:
let user = new User;
akan menyebabkan newpanggilan Usertanpa argumen.
newmengembalikan objek yang dibuatnya, kecuali konstruktor mengembalikan nilai objek , yang dikembalikan sebagai gantinya. Ini adalah kasus tepi yang sebagian besar dapat diabaikan.
Objek yang dibuat oleh fungsi konstruktor mewarisi properti dari properti konstruktor prototype, dan mengembalikan true menggunakan instanceOfoperator pada fungsi konstruktor.
Perilaku di atas dapat gagal jika Anda mengubah nilai prototypeproperti konstruktor secara dinamis setelah menggunakan konstruktor. Melakukannya jarang , dan itu tidak dapat diubah jika konstruktor dibuat menggunakan classkata kunci.
Fungsi konstruktor dapat diperluas menggunakan extendskata kunci.
Fungsi konstruktor tidak dapat kembali nullsebagai nilai kesalahan. Karena ini bukan tipe data objek, diabaikan oleh new.
function User(name, age) {
return {
name,
age,
}
};
let user = User("Tom", 23);
Di sini fungsi pabrik dipanggil tanpa new. Fungsi ini sepenuhnya bertanggung jawab atas penggunaan langsung atau tidak langsung jika argumennya dan jenis objek yang dikembalikannya. Dalam contoh ini ia mengembalikan [Objek objek] sederhana dengan beberapa properti diatur dari argumen.
Mudah menyembunyikan kerumitan implementasi penciptaan objek dari pemanggil. Ini sangat berguna untuk fungsi kode asli di browser.
Fungsi pabrik tidak harus selalu mengembalikan objek dengan tipe yang sama, dan bahkan dapat kembali nullsebagai indikator kesalahan.
Dalam kasus sederhana, fungsi pabrik dapat sederhana dalam struktur dan makna.
Objek yang dikembalikan pada umumnya tidak mewarisi dari prototypeproperti fungsi pabrik , dan kembali falsedari instanceOf factoryFunction.
Fungsi pabrik tidak dapat diperpanjang dengan aman menggunakan extendskata kunci karena objek yang diperluas akan mewarisi dari prototypeproperti fungsi pabrik alih-alih dari prototypeproperti konstruktor yang digunakan oleh fungsi pabrik.
Pabrik "selalu" lebih baik. Saat menggunakan bahasa yang berorientasi objek, maka
Implementasi (objek aktual yang dibuat dengan yang baru) tidak terkena pengguna pabrik / konsumen. Ini berarti bahwa pengembang pabrik dapat memperluas dan membuat implementasi baru selama dia tidak melanggar kontrak ... dan itu memungkinkan bagi konsumen pabrik untuk mendapatkan manfaat dari API baru tanpa harus mengubah kode mereka ... jika mereka menggunakan implementasi baru dan "baru", maka mereka harus pergi dan mengubah setiap baris yang menggunakan "baru" untuk menggunakan implementasi "baru" ... dengan pabrik kode mereka tidak berubah ...
Pabrik - lebih baik daripada yang lainnya - kerangka pegas sepenuhnya dibangun di sekitar ide ini.
Pabrik adalah lapisan abstraksi, dan seperti semua abstraksi mereka memiliki kompleksitas. Ketika menemukan API berbasis pabrik mencari tahu apa itu pabrik untuk API yang diberikan dapat menjadi tantangan bagi konsumen API. Dengan konstruktor, mudah ditemukan adalah sepele.
Saat memutuskan antara ctors dan pabrik, Anda perlu memutuskan apakah kerumitan itu dibenarkan oleh manfaatnya.
Layak dicatat bahwa konstruktor Javascript dapat menjadi pabrik sewenang-wenang dengan mengembalikan sesuatu selain ini atau tidak ditentukan. Jadi di js, Anda bisa mendapatkan yang terbaik dari kedua dunia - API yang dapat ditemukan dan pengumpulan / penyatuan objek.
new, Mengubah perilaku this, Mengubah nilai kembali, Menghubungkan referensi prototipe, Mengaktifkan instanceof(yang terletak dan tidak boleh digunakan untuk tujuan ini). Seolah-olah, semua itu adalah "fitur". Dalam praktiknya, mereka merusak kualitas kode Anda.
Untuk perbedaannya, Eric Elliott mengklarifikasi dengan sangat baik,
Tetapi untuk pertanyaan kedua:
Kapan harus menggunakan salah satu dari yang lain?
Jika Anda berasal dari latar belakang berorientasi objek, fungsi konstruktor terlihat lebih alami bagi Anda. dengan cara ini Anda jangan lupa untuk menggunakan newkata kunci.