ใครสามารถอธิบายความแตกต่างระหว่างฟังก์ชั่นการสร้างและฟังก์ชั่นโรงงานใน Javascript
เมื่อใดจึงควรใช้อันใดอันหนึ่งแทนอันอื่น?
ใครสามารถอธิบายความแตกต่างระหว่างฟังก์ชั่นการสร้างและฟังก์ชั่นโรงงานใน Javascript
เมื่อใดจึงควรใช้อันใดอันหนึ่งแทนอันอื่น?
คำตอบ:
ความแตกต่างพื้นฐานคือฟังก์ชั่นคอนสตรัคเตอร์ใช้กับnew
คำหลัก (ซึ่งทำให้ JavaScript สร้างวัตถุใหม่โดยอัตโนมัติตั้งค่าthis
ภายในฟังก์ชั่นให้กับวัตถุนั้นและส่งคืนวัตถุ):
var objFromConstructor = new ConstructorFunction();
ฟังก์ชั่นจากโรงงานเรียกว่าฟังก์ชั่น "ปกติ":
var objFromFactory = factoryFunction();
แต่เพื่อให้ได้รับการพิจารณาว่าเป็น "โรงงาน" มันจะต้องส่งคืนอินสแตนซ์ใหม่ของวัตถุบางอย่าง: คุณจะไม่เรียกมันว่าฟังก์ชั่น "โรงงาน" ถ้ามันส่งคืนบูลีนหรืออะไรบางอย่าง สิ่งนี้ไม่ได้เกิดขึ้นโดยอัตโนมัติเหมือนกับnew
แต่จะให้ความยืดหยุ่นมากกว่าในบางกรณี
ในตัวอย่างง่าย ๆ ฟังก์ชันที่อ้างถึงข้างต้นอาจมีลักษณะดังนี้:
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;
}
แน่นอนว่าคุณสามารถทำให้ฟังก์ชั่นของโรงงานมีความซับซ้อนได้มากกว่าตัวอย่างง่ายๆ
ข้อดีอย่างหนึ่งของฟังก์ชั่นจากโรงงานคือเมื่อวัตถุที่จะส่งคืนอาจมีหลายประเภทขึ้นอยู่กับพารามิเตอร์บางอย่าง
someMethod
สำหรับวัตถุที่ส่งคืนโดยโรงงานและนั่นคือสิ่งที่มันได้รับหมอกเล็กน้อย ภายในฟังก์ชั่นจากโรงงานหากมีใครทำvar obj = { ... , someMethod: function() {}, ... }
นั่นจะนำไปสู่วัตถุแต่ละชิ้นที่ถูกส่งคืนถือสำเนาที่แตกต่างกันsomeMethod
ซึ่งเป็นสิ่งที่เราอาจไม่ต้องการ นั่นคือที่ที่การใช้งานnew
และprototype
ภายในฟังก์ชั่นจากโรงงานจะช่วยได้
new
กับฟังก์ชั่นคอนสตรัคเตอร์; ฉันคิดว่ามันเป็นสิ่งที่ใคร ๆ จะต้องดูวิธีเปลี่ยน constructors ด้วยตัวอย่างฟังก์ชั่นจากโรงงานและนั่นคือสิ่งที่ฉันคิดว่าจำเป็นต้องมีความสอดคล้องในตัวอย่าง อย่างไรก็ตามคำตอบคือให้ข้อมูลเพียงพอ นี่เป็นเพียงจุดที่ฉันต้องการยกระดับไม่ใช่ว่าฉันจะดึงคุณภาพของคำตอบลงไปในทางใดทางหนึ่ง
new
ภายในหรือใช้Object.create()
เพื่อสร้างวัตถุที่มีต้นแบบที่เฉพาะเจาะจง
หนังสือส่วนใหญ่สอนให้คุณใช้ตัวสร้างและ new
this
หมายถึงวัตถุใหม่
บางคนชอบvar myFoo = new Foo();
อ่านวิธี
รายละเอียดของการสร้างอินสแตนซ์รับการรั่วไหลใน API การโทร (ผ่านnew
ข้อกำหนด) ดังนั้นผู้โทรทั้งหมดจะถูกผนวกเข้ากับการติดตั้ง Constructor หากคุณต้องการความยืดหยุ่นเพิ่มเติมของโรงงานคุณจะต้องปรับโครงสร้างผู้โทรทุกคน (ยอมรับกรณีพิเศษมากกว่ากฎ)
การลืมnew
เป็นข้อผิดพลาดทั่วไปคุณควรพิจารณาเพิ่มการตรวจสอบสำเร็จรูปเพื่อให้แน่ใจว่าตัวสร้างถูกเรียกอย่างถูกต้อง ( if (!(this instanceof Foo)) { return new Foo() }
) แก้ไข: ตั้งแต่ ES6 (ES2015) คุณไม่สามารถลืมnew
ด้วยclass
นวกรรมิกมิฉะนั้นผู้สร้างจะโยนข้อผิดพลาด
หากคุณทำการinstanceof
ตรวจสอบจะทำให้เกิดความคลุมเครือว่าnew
จำเป็นหรือไม่ ในความคิดของฉันมันไม่ควรจะเป็น คุณได้ลัดวงจรnew
ข้อกำหนดอย่างมีประสิทธิภาพซึ่งหมายความว่าคุณสามารถลบข้อเสียเปรียบ # 1 ได้ แต่คุณก็มีฟังก์ชั่นจากโรงงานทั้งหมดยกเว้นชื่อพร้อมตัวเสริมเพิ่มเติมตัวพิมพ์ใหญ่และthis
บริบทที่ยืดหยุ่นน้อยกว่า
แต่ข้อกังวลหลักของฉันคือมันละเมิดหลักการเปิด / ปิด คุณเริ่มส่งออกคอนสตรัคเตอร์ผู้ใช้เริ่มใช้คอนสตรัคเตอร์จากนั้นไปตามถนนที่คุณรู้ว่าคุณต้องการความยืดหยุ่นของโรงงานแทน (ตัวอย่างเช่นเพื่อสลับการนำไปใช้งานเพื่อใช้พูลวัตถุหรืออินสแตนซ์ข้ามบริบทการดำเนินการ มีความยืดหยุ่นในการสืบทอดมากขึ้นโดยใช้ต้นแบบ OO)
แม้ว่าคุณจะติดอยู่ คุณไม่สามารถทำการเปลี่ยนแปลงได้โดยไม่ทำลายโค้ดทั้งหมดที่เรียกตัวสร้างของคุณnew
คุณไม่สามารถทำให้เกิดการเปลี่ยนแปลงโดยไม่ทำลายทุกรหัสที่เรียกคอนสตรัคของคุณด้วยคุณไม่สามารถสลับไปใช้การใช้กลุ่มวัตถุเพื่อเพิ่มประสิทธิภาพเช่น
นอกจากนี้การใช้คอนสตรัคเตอร์จะทำให้คุณเข้าใจผิดinstanceof
ว่าไม่สามารถทำงานข้ามบริบทการดำเนินการได้และจะไม่ทำงานหากตัวสร้างคอนสตรัคเตอร์ของคุณถูกเปลี่ยน มันจะล้มเหลวหากคุณเริ่มกลับมาthis
จากนวกรรมิกของคุณแล้วเปลี่ยนไปใช้การส่งออกวัตถุที่กำหนดเองซึ่งคุณต้องทำเพื่อเปิดใช้งานลักษณะการทำงานเหมือนโรงงานในตัวสร้างของคุณ
รหัสน้อย - ไม่จำเป็นต้องมีสำเร็จรูป
คุณสามารถคืนค่าวัตถุใด ๆ และใช้ต้นแบบใดก็ได้ - ให้ความยืดหยุ่นมากขึ้นในการสร้างวัตถุประเภทต่าง ๆ ที่ใช้ API เดียวกัน ตัวอย่างเช่นโปรแกรมเล่นสื่อที่สามารถสร้างอินสแตนซ์ของทั้ง HTML5 และเครื่องเล่นแฟลชหรือไลบรารีเหตุการณ์ที่สามารถปล่อยกิจกรรม DOM หรือเว็บซ็อกเก็ตเหตุการณ์ โรงงานยังสามารถยกตัวอย่างวัตถุข้ามบริบทการดำเนินการใช้ประโยชน์จากกลุ่มวัตถุและอนุญาตให้มีรูปแบบการสืบทอดต้นแบบที่ยืดหยุ่นมากขึ้น
คุณไม่จำเป็นต้องแปลงจากโรงงานเป็นคอนสตรัคเตอร์ดังนั้นการปรับโครงสร้างจะไม่เป็นปัญหาอีกต่อไป
new
ไม่มีความคลุมเครือเกี่ยวกับการใช้ อย่า (มันจะทำให้this
พฤติกรรมไม่ดีดูจุดต่อไป)
this
พฤติกรรมที่มันปกติ - เพื่อให้คุณสามารถใช้มันในการเข้าถึงวัตถุแม่ (ตัวอย่างเช่นภายในplayer.create()
, this
หมายถึงplayer
. เช่นเดียวกับวิธีการอุทธรณ์อื่น ๆ จะcall
และapply
ยังมอบหมายthis
เป็นไปตามคาดหากคุณเก็บต้นแบบในวัตถุแม่ที่. สามารถเป็นวิธีที่ดีในการสลับการทำงานแบบไดนามิกและเปิดใช้งานความหลากหลายที่หลากหลายสำหรับการสร้างอินสแตนซ์วัตถุของคุณ
ไม่มีความเคลือบแคลงเกี่ยวกับว่าจะลงทุนหรือไม่ อย่า เครื่องมือผ้าสำลีจะบ่นและจากนั้นคุณจะถูกล่อลวงให้พยายามใช้new
และจากนั้นคุณจะยกเลิกประโยชน์ที่ได้อธิบายไว้ข้างต้น
บางคนชอบวิธีvar myFoo = foo();
หรือvar myFoo = foo.create();
อ่าน
new
ไม่ทำงานตามที่คาดไว้ (ดูด้านบน) วิธีแก้ปัญหา: อย่าใช้มัน
this
ไม่ได้อ้างถึงออบเจ็กต์ใหม่ (แทนหากตัวสร้างถูกเรียกด้วยเครื่องหมายจุดหรือเครื่องหมายวงเล็บเหลี่ยมเช่น foo.bar () - this
หมายถึงfoo
- เหมือนกับวิธี JavaScript อื่น ๆ - ดูประโยชน์)
new
ละเมิดหลักการเปิด / ปิด ดูmedium.com/javascript-scene/…สำหรับการสนทนาที่ใหญ่กว่าความคิดเห็นเหล่านี้อนุญาต
new
คำหลักฉันไม่เชื่อว่าnew
คำหลักนั้นมีความสามารถในการอ่านเพิ่มเติม IMO ดูเหมือนว่าโง่ที่จะกระโดดผ่านห่วงเพื่อให้ผู้โทรพิมพ์มากขึ้น
ตัวสร้างส่งคืนอินสแตนซ์ของคลาสที่คุณเรียกใช้ ฟังก์ชั่นจากโรงงานสามารถคืนสิ่งใดก็ได้ คุณจะใช้ฟังก์ชั่นจากโรงงานเมื่อคุณต้องการคืนค่าโดยพลการหรือเมื่อคลาสมีกระบวนการตั้งค่าขนาดใหญ่
function User(name) {
this.name = name;
this.isAdmin = false;
}
let user = new User("Jack");
new
สร้างต้นแบบของวัตถุUser.prototype
และเรียกใช้User
กับวัตถุที่สร้างขึ้นเป็นthis
ค่าของมัน
new
ถือว่านิพจน์อาร์กิวเมนต์สำหรับตัวถูกดำเนินการเป็นตัวเลือก:
let user = new User;
จะทำให้new
การโทรUser
โดยไม่มีข้อโต้แย้ง
new
ส่งคืนวัตถุที่สร้างขึ้นเว้นแต่ว่าตัวสร้างส่งกลับค่าวัตถุซึ่งจะถูกส่งกลับแทน นี่คือกรณีขอบซึ่งส่วนใหญ่สามารถละเว้น
วัตถุที่สร้างโดยฟังก์ชั่นตัวสร้างสืบทอดคุณสมบัติจากคุณสมบัติของตัวสร้างprototype
และส่งคืนค่าจริงโดยใช้ตัวinstanceOf
ดำเนินการบนฟังก์ชันตัวสร้าง
พฤติกรรมข้างต้นอาจล้มเหลวหากคุณเปลี่ยนค่าของprototype
คุณสมบัติของตัวสร้างแบบไดนามิกหลังจากใช้ตัวสร้างแล้ว การทำเช่นนี้หายากและไม่สามารถเปลี่ยนแปลงได้หากตัวสร้างถูกสร้างขึ้นโดยใช้class
คำหลัก
ฟังก์ชันตัวสร้างสามารถขยายได้โดยใช้extends
คำสำคัญ
ฟังก์ชันตัวสร้างไม่สามารถส่งคืนnull
เป็นค่าความผิดพลาดได้ new
เพราะมันไม่ได้เป็นชนิดข้อมูลวัตถุนั้นจะถูกปฏิเสธโดย
function User(name, age) {
return {
name,
age,
}
};
let user = User("Tom", 23);
new
นี่คือการทำงานของโรงงานที่เรียกว่าโดยไม่ต้อง ฟังก์ชั่นมีความรับผิดชอบทั้งหมดสำหรับการใช้งานโดยตรงหรือโดยอ้อมหากข้อโต้แย้งและประเภทของวัตถุที่ส่งกลับ ในตัวอย่างนี้ส่งคืน [Object Object] แบบง่ายพร้อมคุณสมบัติบางอย่างที่ตั้งค่าจากอาร์กิวเมนต์
ซ่อนความซับซ้อนของการนำไปปฏิบัติของการสร้างวัตถุจากผู้โทรได้อย่างง่ายดาย สิ่งนี้มีประโยชน์อย่างยิ่งสำหรับฟังก์ชันโค้ดเนทีฟในเบราว์เซอร์
ฟังก์ชั่นจากโรงงานไม่จำเป็นต้องส่งคืนวัตถุประเภทเดียวกันเสมอไปและอาจส่งคืนnull
เป็นตัวบ่งชี้ข้อผิดพลาดได้
ในกรณีง่าย ๆ ฟังก์ชั่นของโรงงานสามารถทำได้ง่ายในโครงสร้างและความหมาย
วัตถุกลับไม่ได้รับมรดกโดยทั่วไปจากฟังก์ชั่นของโรงงานprototype
ทรัพย์สินและผลตอบแทนจากfalse
instanceOf factoryFunction
ไม่สามารถขยายฟังก์ชั่นจากโรงงานได้อย่างปลอดภัยโดยใช้extends
คีย์เวิร์ดเนื่องจากวัตถุที่ขยายจะสืบทอดมาจากprototype
คุณสมบัติฟังก์ชั่นจากโรงงานแทนที่จะเป็นจากprototype
คุณสมบัติของคอนสตรัคเตอร์ที่ใช้โดยฟังก์ชันโรงงาน
โรงงานจะดีกว่าเสมอ เมื่อใช้ภาษาที่มุ่งวัตถุแล้ว
การใช้งาน (วัตถุจริงที่สร้างขึ้นด้วยใหม่) จะไม่ถูกเปิดเผยต่อผู้ใช้ / ผู้บริโภคในโรงงาน ซึ่งหมายความว่าผู้พัฒนาโรงงานสามารถขยายและสร้างการใช้งานใหม่ตราบใดที่เขา / เธอไม่ผิดสัญญา ... และช่วยให้ผู้บริโภคโรงงานได้รับประโยชน์จาก API ใหม่โดยไม่ต้องเปลี่ยนรหัส ... หากพวกเขาใช้ใหม่และการใช้งาน "ใหม่" มาพร้อมพวกเขาจะต้องไปและเปลี่ยนแปลงทุกบรรทัดที่ใช้ "ใหม่" เพื่อใช้การดำเนินการ "ใหม่" ... กับโรงงานรหัสของพวกเขาจะไม่เปลี่ยนแปลง ...
โรงงาน - ดีกว่าสิ่งอื่นใดทั้งหมด - โครงร่างสปริงสร้างขึ้นโดยสมบูรณ์ตามแนวความคิดนี้
โรงงานเป็นชั้นของสิ่งที่เป็นนามธรรมและเช่นเดียวกับสิ่งที่เป็นนามธรรมทั้งหมดพวกเขามี a.cost ในความซับซ้อน เมื่อเผชิญหน้ากับ API ที่ใช้มาจากโรงงานการค้นหาว่าโรงงานนั้นมีไว้สำหรับ API ที่กำหนดนั้นสามารถท้าทายผู้ใช้ API ได้อย่างไร ด้วยการค้นพบตัวสร้างเป็นเรื่องไม่สำคัญ
ในการตัดสินใจเลือกระหว่างผู้ว่าจ้างและโรงงานคุณจำเป็นต้องตัดสินใจว่าความซับซ้อนนั้นมีความชอบธรรมโดยผลประโยชน์หรือไม่
น่าสังเกตว่าตัวสร้าง Javascript อาจเป็นโรงงานที่กำหนดเองได้โดยส่งคืนสิ่งอื่นนอกเหนือจากนี้หรือไม่ได้กำหนดไว้ ดังนั้นใน js คุณจะได้รับทั้งสองสิ่งที่ดีที่สุดในโลก - API ที่ค้นพบได้และการรวมวัตถุ / แคช
new
, การเปลี่ยนแปลงพฤติกรรมของthis
, การปรับเปลี่ยนค่าส่งคืน, การเชื่อมต่อการอ้างอิงต้นแบบ, การเปิดใช้งานinstanceof
(ซึ่งอยู่และไม่ควรใช้เพื่อจุดประสงค์นี้) เห็นได้ชัดว่าทั้งหมดคือ "คุณสมบัติ" ในทางปฏิบัติมันจะทำลายคุณภาพของรหัสของคุณ
สำหรับความแตกต่าง Eric Elliott ชี้แจงได้ดีมาก
แต่สำหรับคำถามที่สอง:
เมื่อใดจึงควรใช้อันใดอันหนึ่งแทนอันอื่น?
หากคุณมาจากพื้นหลังเชิงวัตถุฟังก์ชันตัวสร้างจะดูเป็นธรรมชาติมากขึ้นสำหรับคุณ ด้วยวิธีนี้คุณไม่ควรลืมที่จะใช้new
คำหลัก