Adakah yang bisa menjelaskan perbedaan antara fungsi konstruktor dan fungsi pabrik di Javascript.
Kapan harus menggunakan salah satu dari yang lain?
sumber
Adakah yang bisa menjelaskan perbedaan antara fungsi konstruktor dan fungsi pabrik di Javascript.
Kapan harus menggunakan salah satu dari yang lain?
Perbedaan dasarnya adalah bahwa fungsi konstruktor digunakan dengan new
kata kunci (yang menyebabkan JavaScript secara otomatis membuat objek baru, mengatur this
dalam 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.
someMethod
untuk objek yang dikembalikan oleh pabrik, dan di sanalah ia menjadi sedikit berkabut. Di dalam fungsi pabrik, jika ada begitu sajavar obj = { ... , someMethod: function() {}, ... }
, itu akan menyebabkan setiap objek kembali memegang salinan berbedasomeMethod
yang merupakan sesuatu yang mungkin tidak kita inginkan. Di situlah menggunakannew
danprototype
di dalam fungsi pabrik akan membantu.new
fungsi 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.new
internal, atau menggunakanObject.create()
untuk membuat objek dengan prototipe tertentu.Manfaat menggunakan konstruktor
Sebagian besar buku mengajarkan Anda untuk menggunakan konstruktor dan
new
this
merujuk ke objek baruBeberapa orang suka cara
var myFoo = new Foo();
membaca.Kekurangannya
Detail Instansiasi bocor ke API panggilan (melalui
new
persyaratan), 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
new
adalah 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 melupakannew
denganclass
konstruktor, atau konstruktor akan melempar kesalahan.Jika Anda melakukan
instanceof
pemeriksaan, itu meninggalkan ambiguitas apakahnew
diperlukan atau tidak . Menurut saya, seharusnya tidak. Anda secara efektif menghubung pendeknew
persyaratan, yang berarti Anda dapat menghapus kekurangan # 1. Tapi kemudian Anda baru saja mendapatkan fungsi pabrik di semua kecuali nama , dengan pelat tambahan, huruf kapital, danthis
konteks yang kurang fleksibel .Konstruktor melanggar Prinsip Terbuka / Tertutup
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
instanceof
yang tidak berfungsi di seluruh konteks eksekusi, dan tidak berfungsi jika prototipe konstruktor Anda diganti. Ini juga akan gagal jika Anda mulai kembalithis
dari konstruktor Anda, dan kemudian beralih ke mengekspor objek sewenang-wenang, yang harus Anda lakukan untuk mengaktifkan perilaku seperti pabrik di konstruktor Anda.Manfaat menggunakan pabrik
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 membuatthis
berperilaku buruk, lihat poin berikutnya).this
berperilaku seperti biasa - sehingga Anda dapat menggunakannya untuk mengakses objek induk (misalnya, di dalamplayer.create()
,this
mengacu padaplayer
, sama seperti metode doa lainnya akan,call
danapply
juga menugaskan kembalithis
, 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();
atauvar myFoo = foo.create();
membaca.Kekurangannya
new
tidak berperilaku seperti yang diharapkan (lihat di atas). Solusi: jangan gunakan itu.this
tidak merujuk ke objek baru (sebagai gantinya, jika konstruktor dipanggil dengan notasi titik atau notasi braket persegi, misalnya foo.bar () -this
mengacu padafoo
- sama seperti setiap metode JavaScript lainnya - lihat manfaatnya).sumber
new
melanggar prinsip terbuka / tertutup. Lihat medium.com/javascript-scene/… untuk diskusi yang jauh lebih besar daripada komentar ini.new
kata kunci, saya tidak percaya bahwanew
kata 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.
sumber
Contoh fungsi Konstruktor
new
membuat objek yang di-prototipeUser.prototype
dan memanggilUser
dengan objek yang dibuat sebagaithis
nilainya.new
memperlakukan ekspresi argumen untuk operan sebagai opsional:akan menyebabkan
new
panggilanUser
tanpa argumen.new
mengembalikan objek yang dibuatnya, kecuali konstruktor mengembalikan nilai objek , yang dikembalikan sebagai gantinya. Ini adalah kasus tepi yang sebagian besar dapat diabaikan.Pro dan kontra
Objek yang dibuat oleh fungsi konstruktor mewarisi properti dari properti konstruktor
prototype
, dan mengembalikan true menggunakaninstanceOf
operator pada fungsi konstruktor.Perilaku di atas dapat gagal jika Anda mengubah nilai
prototype
properti konstruktor secara dinamis setelah menggunakan konstruktor. Melakukannya jarang , dan itu tidak dapat diubah jika konstruktor dibuat menggunakanclass
kata kunci.Fungsi konstruktor dapat diperluas menggunakan
extends
kata kunci.Fungsi konstruktor tidak dapat kembali
null
sebagai nilai kesalahan. Karena ini bukan tipe data objek, diabaikan olehnew
.Contoh fungsi Pabrik
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.Pro dan kontra
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
null
sebagai indikator kesalahan.Dalam kasus sederhana, fungsi pabrik dapat sederhana dalam struktur dan makna.
Objek yang dikembalikan pada umumnya tidak mewarisi dari
prototype
properti fungsi pabrik , dan kembalifalse
dariinstanceOf factoryFunction
.Fungsi pabrik tidak dapat diperpanjang dengan aman menggunakan
extends
kata kunci karena objek yang diperluas akan mewarisi dariprototype
properti fungsi pabrik alih-alih dariprototype
properti konstruktor yang digunakan oleh fungsi pabrik.sumber
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.
sumber
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.
sumber
new
, Mengubah perilakuthis
, Mengubah nilai kembali, Menghubungkan referensi prototipe, Mengaktifkaninstanceof
(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:
Jika Anda berasal dari latar belakang berorientasi objek, fungsi konstruktor terlihat lebih alami bagi Anda. dengan cara ini Anda jangan lupa untuk menggunakan
new
kata kunci.sumber