Jawaban:
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 saja var obj = { ... , someMethod: function() {}, ... }
, itu akan menyebabkan setiap objek kembali memegang salinan berbeda someMethod
yang merupakan sesuatu yang mungkin tidak kita inginkan. Di situlah menggunakan new
dan prototype
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 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 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 melupakan new
dengan class
konstruktor, atau konstruktor akan melempar kesalahan.
Jika Anda melakukan instanceof
pemeriksaan, itu meninggalkan ambiguitas apakah new
diperlukan atau tidak . Menurut saya, seharusnya tidak. Anda secara efektif menghubung pendek new
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, dan this
konteks 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 instanceof
yang tidak berfungsi di seluruh konteks eksekusi, dan tidak berfungsi jika prototipe konstruktor Anda diganti. Ini juga akan gagal jika Anda mulai kembali this
dari 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 this
berperilaku buruk, lihat poin berikutnya).
this
berperilaku seperti biasa - sehingga Anda dapat menggunakannya untuk mengakses objek induk (misalnya, di dalam player.create()
, this
mengacu pada player
, sama seperti metode doa lainnya akan, call
dan apply
juga 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.
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 pada foo
- sama seperti setiap metode JavaScript lainnya - lihat manfaatnya).
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 bahwa new
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.
function User(name) {
this.name = name;
this.isAdmin = false;
}
let user = new User("Jack");
new
membuat objek yang di-prototipe User.prototype
dan memanggil User
dengan objek yang dibuat sebagai this
nilainya.
new
memperlakukan ekspresi argumen untuk operan sebagai opsional:
let user = new User;
akan menyebabkan new
panggilan User
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.
Objek yang dibuat oleh fungsi konstruktor mewarisi properti dari properti konstruktor prototype
, dan mengembalikan true menggunakan instanceOf
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 menggunakan class
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 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 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 kembali false
dari instanceOf factoryFunction
.
Fungsi pabrik tidak dapat diperpanjang dengan aman menggunakan extends
kata kunci karena objek yang diperluas akan mewarisi dari prototype
properti fungsi pabrik alih-alih dari prototype
properti 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 new
kata kunci.