เมื่อเร็ว ๆ นี้ฉันกำลังดู "JavaScript Advanced Programming" (ฉบับที่สอง)
การสร้างวัตถุในจาวาสคริปต์
•โหมดโรงงาน
•โหมดตัวสร้าง
•โหมดต้นแบบ
•การรวมตัวสร้างและรูปแบบต้นแบบ
•โหมดไดนามิกต้นแบบ
ภาษาที่มุ่งเน้นวัตถุส่วนใหญ่มีแนวคิดของคลาสซึ่งสามารถสร้างวัตถุหลายอย่างที่มีวิธีการและแอตทริบิวต์เดียวกัน แม้ว่าในทางเทคนิคแล้ว JavaScript เป็นภาษาที่มุ่งเน้นวัตถุ JavaScript ไม่มีแนวคิดของชั้นเรียนทุกอย่างเป็นวัตถุ วัตถุใด ๆ เป็นอินสแตนซ์ของประเภทการอ้างอิงที่แน่นอนและถูกสร้างขึ้นผ่านประเภทการอ้างอิงที่มีอยู่ ประเภทการอ้างอิงสามารถเป็นแบบดั้งเดิมหรือปรับแต่งได้ ประเภทการอ้างอิงดั้งเดิมคือ: วัตถุ, อาร์เรย์, ข้อมูล, regexp, ฟังก์ชั่น - ประเภทการอ้างอิงคือโครงสร้างข้อมูลที่จัดระเบียบข้อมูลและฟังก์ชั่นเข้าด้วยกันซึ่งมักเรียกว่าคลาส ในจาวาสคริปต์ที่ขาดแนวคิดของชั้นเรียนปัญหาที่ต้องแก้ไขคือวิธีการสร้างวัตถุอย่างมีประสิทธิภาพ
1.1.0. วิธีการทั่วไปสำหรับการสร้างวัตถุ
var person = {}; // การเป็นตัวแทนตามตัวอักษรของวัตถุเทียบเท่ากับ var person = new objcect (); person.name = 'evansdiy'; person.age = '22'; person.friends = ['ajiao', 'tiantian', 'pangzi']; person.logname = function () {console.log (this.name);}ขึ้นอยู่กับประเภทการอ้างอิงวัตถุวัตถุถูกสร้างขึ้นซึ่งมีคุณสมบัติสี่คุณสมบัติหนึ่งในนั้นคือวิธีการ หากคุณต้องการหลาย ๆ กรณีเช่นบุคคลจะมีรหัสที่ซ้ำกันจำนวนมาก
1.1.1. รูปแบบโรงงาน
สร้างวัตถุด้วยฟังก์ชั่นที่สามารถมีรายละเอียดของวัตถุแล้วส่งคืนวัตถุ
ฟังก์ชั่นบุคคล (ชื่อ, อายุ, เพื่อน) {var o = {ชื่อ: ชื่อ, อายุ: อายุ, เพื่อน: เพื่อน, logname: function () {console.log (this.name); - return o;} var person1 = person ('evansdiy', '22', ['ajiao', 'tiantian', 'pangzi']);ทุกครั้งที่มีการเรียกฟังก์ชั่นของบุคคลวัตถุใหม่จะถูกสร้างขึ้นผ่านวัตถุ o ภายในฟังก์ชั่นแล้วส่งคืน นอกเหนือจากนี้วัตถุภายในนี้มีอยู่สำหรับการสร้างวัตถุใหม่ไม่มีวัตถุประสงค์อื่น นอกจากนี้ยังเป็นไปไม่ได้ที่จะกำหนดประเภทของวัตถุที่สร้างขึ้นโดยโหมดโรงงาน
1.1.2. โหมดตัวสร้าง
ฟังก์ชั่นบุคคล (ชื่อ, อายุ, งาน) {this.name = name; this.age = อายุ; this.job = งาน; this.logname = function () {console.log (this.name); }} // สร้างอินสแตนซ์ของบุคคลผ่านตัวดำเนินการใหม่ var person1 = บุคคลใหม่ ('boy-a', '22', 'คนงาน'); var person2 = บุคคลใหม่ ('girl-b', '23', 'ครู'); person1.logname (); //boy-aperson2.logname (); // girl-aการเปรียบเทียบโหมดโรงงานเราสามารถพบได้ว่าไม่จำเป็นต้องสร้างวัตถุระดับกลางที่นี่ไม่มีผลตอบแทน นอกจากนี้อินสแตนซ์ของตัวสร้างสามารถระบุได้ว่าเป็นประเภทเฉพาะซึ่งแก้ปัญหาการจดจำวัตถุ (โดยการตรวจสอบแอตทริบิวต์ตัวสร้างของอินสแตนซ์หรือใช้ตัวดำเนินการอินสแตนซ์ของตัวดำเนินการเพื่อตรวจสอบว่าอินสแตนซ์ถูกสร้างขึ้นโดยตัวสร้างหรือไม่)
console.log (person1.constructor == บุคคล); // ตัวสร้างอยู่ในตัวสร้างตัวสร้างและชี้ไปที่ตัวสร้างและผลลัพธ์เป็นจริง
console.log (person1 instanceof person); // ใช้ตัวดำเนินการอินสแตนซ์ของตัวดำเนินการเพื่อตรวจสอบว่า Person1 เป็นตัวอย่างของตัวสร้างบุคคลหรือไม่ แต่รูปแบบตัวสร้างยังมีปัญหาของตัวเอง ในความเป็นจริงวิธี logname จะถูกสร้างขึ้นใหม่หนึ่งครั้งในแต่ละอินสแตนซ์ ควรสังเกตว่าวิธีการที่สร้างโดยอินสแตนซ์นั้นไม่เท่ากันและรหัสต่อไปนี้จะได้รับเท็จ:
console.log (person1.logname == person2.logname); // false เราสามารถย้ายวิธีการนอกคอนสตรัคเตอร์ (เปลี่ยนเป็นฟังก์ชั่นทั่วโลก) เพื่อแก้ปัญหานี้:
ฟังก์ชั่น logName () {console.log (this.name);} function logage () {console.log (this.age);}อย่างไรก็ตามฟังก์ชั่นทั่วโลกที่สร้างขึ้นทั่วโลกสามารถเรียกได้โดยกรณีที่สร้างขึ้นโดยบุคคลซึ่งไม่สมจริงเล็กน้อย หากมีวิธีการมากมายพวกเขายังคงต้องกำหนดทีละวิธีโดยขาดการห่อหุ้ม
1.1.3. โหมดต้นแบบ
แต่ละฟังก์ชั่นใน JavaScript มีตัวชี้ไปยังแอตทริบิวต์ต้นแบบ (เบราว์เซอร์ส่วนใหญ่สามารถเข้าถึงได้ผ่านแอตทริบิวต์ภายใน __proto__) แอตทริบิวต์ต้นแบบเป็นวัตถุที่มีคุณสมบัติและวิธีการที่ใช้ร่วมกันโดยทุกกรณีที่สร้างโดยประเภทอ้างอิงบางประเภท
ฟังก์ชั่นบุคคล () {} person.name = 'evansdiy'; person.prototype.friends = ['ajiao', 'Jianjian', 'pangzi']; person.prototype.logname = function () {console.log (this.name);รหัสข้างต้นทำสิ่งเหล่านี้:
1. กำหนดบุคคลที่สร้าง ฟังก์ชั่นบุคคลจะได้รับคุณสมบัติต้นแบบโดยอัตโนมัติ คุณสมบัตินี้มีเฉพาะคุณสมบัติตัวสร้างที่ชี้ไปที่บุคคลโดยค่าเริ่มต้น
2. เพิ่มแอตทริบิวต์สามตัวผ่าน person.prototype หนึ่งในนั้นใช้เป็นวิธีการ;
3. สร้างอินสแตนซ์ของบุคคลจากนั้นเรียกใช้เมธอด logName () บนอินสแตนซ์ -
สิ่งที่คุณต้องทราบที่นี่คือกระบวนการเรียกใช้วิธี logName ():
1. ค้นหาเมธอด logname () บนอินสแตนซ์ Person1 และพบว่าไม่มีวิธีการดังกล่าวดังนั้นฉันจึงย้อนกลับไปที่ต้นแบบของ Person1
2. มองหาวิธี logame () บนต้นแบบของ person1 มีวิธีนี้ ดังนั้นเราจึงเรียกวิธีนี้ตามกระบวนการค้นหาดังกล่าว เราสามารถป้องกันไม่ให้อินสแตนซ์เข้าถึงแอตทริบิวต์ชื่อเดียวกันบนต้นแบบโดยกำหนดแอตทริบิวต์ชื่อเดียวกันในต้นแบบในอินสแตนซ์ ควรสังเกตว่าการทำเช่นนั้นจะไม่ลบแอตทริบิวต์ชื่อเดียวกันบนต้นแบบ แต่จะป้องกันไม่ให้อินสแตนซ์เข้าถึงเท่านั้น
var person2 = คนใหม่ ();
person2.name = 'laocai'; หากเราไม่ต้องการคุณสมบัติในอินสแตนซ์อีกต่อไปเราสามารถลบออกผ่านตัวดำเนินการลบ
ลบ person2.name; ใช้การวนรอบสำหรับการระบุแอตทริบิวต์ทั้งหมดที่อินสแตนซ์สามารถเข้าถึงได้ (ไม่ว่าแอตทริบิวต์จะมีอยู่ในอินสแตนซ์หรือในต้นแบบ):
สำหรับ (i in person1) {console.log (i);}ในเวลาเดียวกันคุณสามารถใช้วิธี HASOWNPROPERTY () เพื่อตรวจสอบว่ามีคุณสมบัติบางอย่างในอินสแตนซ์หรือในต้นแบบ เฉพาะเมื่อคุณสมบัติที่มีอยู่ในอินสแตนซ์จะถูกส่งคืน:
console.log (person1.hasownproperty ('ชื่อ')); // true! HasownProperty มาจากต้นแบบของวัตถุและเป็นวิธีเดียวใน JavaScript ที่จะไม่ค้นหาห่วงโซ่ต้นแบบเมื่อประมวลผลคุณสมบัติ [ผ่าน JavaScript Secret Garden] นอกจากนี้คุณยังสามารถใช้วิธีการในผู้ให้บริการและ HasownProperty () เพื่อตรวจสอบว่ามีคุณสมบัติบางอย่างในอินสแตนซ์หรือในต้นแบบ:
console.log (('เพื่อน' ใน person1) &&! person1.hasownproperty ('เพื่อน')) ก่อนกำหนดว่า person1 สามารถเข้าถึงทรัพย์สินของเพื่อนได้หรือไม่ ถ้าเป็นไปได้ให้พิจารณาว่าคุณสมบัตินี้มีอยู่ในอินสแตนซ์ (หมายเหตุก่อนหน้า!) หากไม่มีอยู่ในอินสแตนซ์ก็หมายความว่าคุณสมบัตินี้มีอยู่ในต้นแบบ ดังที่ได้กล่าวไว้ก่อนหน้านี้ต้นแบบยังเป็นวัตถุดังนั้นเราจึงสามารถเขียนต้นแบบโดยใช้การแสดงตามตัวอักษรของวัตถุ วิธีการเขียนก่อนหน้าของการเพิ่มรหัสลงในต้นแบบสามารถแก้ไขได้เป็น:
person.prototype = {ชื่อ: 'evansdiy', เพื่อน: ['ajiao', 'Jianjian', 'pangzi'], logname: function () {console.log (this.name); -เนื่องจากไวยากรณ์ตามตัวอักษรของวัตถุเขียนต้นแบบต้นแบบทั้งหมดใหม่แอตทริบิวต์คอนสตรัคเตอร์ที่ได้รับโดยค่าเริ่มต้นเมื่อสร้างตัวสร้างจะชี้ไปที่ตัวสร้างวัตถุ:
// หลังจากวัตถุเขียนตัวอักษรใหม่
console.log (person1.constructor); // object อย่างไรก็ตามผู้ดำเนินการอินสแตนซ์ของจะยังคงส่งคืนผลลัพธ์ที่ต้องการ:
// หลังจากวัตถุเขียนตัวอักษรใหม่
console.log (person1 instanceof person); // จริงแน่นอนคุณสามารถตั้งค่าตัวสร้างในต้นแบบเพื่อแก้ปัญหานี้ด้วยตนเอง
person.prototype = {constructor: person, ...... }หากวัตถุต้นแบบได้รับการแก้ไขหลังจากสร้างอินสแตนซ์ของวัตถุการปรับเปลี่ยนต้นแบบจะถูกสะท้อนทันทีในทุกอินสแตนซ์ของวัตถุ:
ฟังก์ชันบุคคล () {}; var person1 = บุคคลใหม่ (); person.prototype.name = 'evansdiy'; console.log (person1.name); // 'evansdiy'การเชื่อมต่อระหว่างอินสแตนซ์และต้นแบบเป็นเพียงตัวชี้ไม่ใช่สำเนาของต้นแบบ ต้นแบบเป็นกระบวนการค้นหาจริง ๆ การดัดแปลงใด ๆ ที่ทำกับวัตถุต้นแบบจะสะท้อนให้เห็นในทุกอินสแตนซ์ของวัตถุแม้ว่าจะมีการแก้ไขต้นแบบหลังจากสร้างอินสแตนซ์ จะเกิดอะไรขึ้นถ้าวัตถุต้นแบบเขียนใหม่หลังจากสร้างอินสแตนซ์วัตถุ
ฟังก์ชั่นบุคคล () {}; var person1 = new person1 (); // อินสแตนซ์ที่สร้างขึ้นหมายถึงต้นแบบต้นแบบ // person person.prototype = {เพื่อน: ['ajiao', 'Jianjian', 'pangzi']} var person2 = บุคคลใหม่ () console.log (person1.friends);รหัสข้างต้นจะมีข้อผิดพลาดที่ไม่ได้กำหนดเมื่อดำเนินการกับบรรทัดสุดท้าย หากเราใช้สำหรับการระบุคุณสมบัติที่เข้าถึงได้ใน Person1 เราจะพบว่าไม่มีอะไรอยู่ข้างใน แต่ Person2 สามารถเข้าถึงแอตทริบิวต์ของเพื่อนบนต้นแบบได้ - การเขียนต้นแบบใหม่ตัดการเชื่อมต่อระหว่างต้นแบบที่มีอยู่และอินสแตนซ์วัตถุทั้งหมดที่สร้างขึ้นก่อน ต้นแบบของอินสแตนซ์ของวัตถุที่สร้างขึ้นก่อนยังคงอยู่ที่นั่น แต่มันเก่า
// เมื่อสร้าง person1 วัตถุต้นแบบยังไม่ได้รับการเขียนใหม่ ดังนั้นตัวสร้างในวัตถุต้นแบบยังคงเป็นบุคคลเริ่มต้น () console.log (person1.constructor); // person () // แต่ตัวสร้างของ person2 ชี้ไปที่วัตถุ () console.log (person2.constructor); // object ()
ควรสังเกตว่ารูปแบบต้นแบบจะไม่สนใจกระบวนการผ่านพารามิเตอร์สำหรับตัวสร้างและอินสแตนซ์ทั้งหมดได้รับค่าแอตทริบิวต์เดียวกัน ในเวลาเดียวกันมีปัญหาใหญ่เกี่ยวกับรูปแบบต้นแบบนั่นคือค่าประเภทการอ้างอิงในวัตถุต้นแบบจะถูกแชร์โดยทุกกรณีและการปรับเปลี่ยนค่าประเภทอ้างอิงจะถูกสะท้อนในทุกอินสแตนซ์วัตถุทั้งหมด
Function Person () {}; person.prototype = {เพื่อน: ['ajiao', 'tiantian', 'pangzi']} var person1 = บุคคลใหม่ (); var person2 = ใหม่ person (); person1.friends.push ('laocai'); console.log (person2.friends); // ['ajiao', 'tiantian', 'pangzi', 'laocai']การปรับเปลี่ยนค่าประเภทการอ้างอิงของเพื่อน 1 คนหมายความว่าเพื่อนในบุคคล 2 จะเปลี่ยนไปเช่นกัน ในความเป็นจริงเพื่อนที่บันทึกไว้ในต้นแบบเป็นเพียงตัวชี้ไปที่ค่าเพื่อนในกอง (ความยาวของตัวชี้นี้ได้รับการแก้ไขและบันทึกไว้บนสแต็ก) เมื่ออินสแตนซ์เข้าถึงค่าประเภทการอ้างอิงผ่านต้นแบบจะสามารถเข้าถึงได้โดยตัวชี้แทนที่จะเข้าถึงสำเนาบนอินสแตนซ์ที่เกี่ยวข้อง (ไม่มีการคัดลอก)
1.1.4. สร้างวัตถุร่วมกับตัวสร้างและรูปแบบต้นแบบ
การรวมข้อดีของคอนสตรัคเตอร์และโหมดต้นแบบประกอบขึ้นสำหรับข้อบกพร่องที่เกี่ยวข้องโดยใช้ตัวสร้างเพื่อผ่านพารามิเตอร์การเริ่มต้นกำหนดคุณลักษณะอินสแตนซ์ในพวกเขาและใช้ต้นแบบเพื่อกำหนดวิธีการทั่วไปและคุณลักษณะสาธารณะ โหมดนี้ใช้กันอย่างแพร่หลายมากที่สุด
ฟังก์ชั่นบุคคล (ชื่ออายุ) {this.name = name; this.age = อายุ; this.friends = ['ajiao', 'Jianjian', 'pangzi'];} person.prototype = {constructor: person, logname: function () {console.log (this.name); }} var person1 = บุคคลใหม่ ('evansdiy', '22'); var person2 = บุคคลใหม่ ('amy', '21'); person1.logname (); // 'evansdiy'person1.friends.push (' haixao '); console.log1.1.5. โหมดไดนามิกต้นแบบ
โหมดไดนามิกต้นแบบห่อหุ้มข้อมูลทั้งหมดที่จำเป็นในตัวสร้างและใช้คำสั่ง IF เพื่อพิจารณาว่ามีคุณสมบัติบางอย่างในต้นแบบหรือไม่ หากไม่มีอยู่ (เมื่อตัวสร้างถูกเรียกเป็นครั้งแรก) ให้ดำเนินการรหัสการเริ่มต้นต้นแบบภายในคำสั่ง IF
ฟังก์ชั่นบุคคล (ชื่ออายุ) {this.name = name; this.age = อายุ; if (typeof this.logname! = 'function') {person.prototype.logname = function () {console.log (this.name); - person.prototype.logage = function () {console.log (this.age); - - };} var person1 = บุคคลใหม่ ('evansdiy', '22'); // ตัวสร้างถูกเรียกเป็นครั้งแรกและต้นแบบได้รับการแก้ไขในเวลานี้ var person2 = บุคคลใหม่ ('amy', '21'); // เมธอด logname () มีอยู่แล้วและต้นแบบจะไม่ถูกแก้ไขอีกครั้งควรสังเกตว่ารูปแบบนี้ไม่สามารถใช้ไวยากรณ์ตามตัวอักษรวัตถุเพื่อเขียนวัตถุต้นแบบ (สิ่งนี้จะแทนที่วัตถุต้นแบบ) หากต้นแบบถูกเขียนใหม่วัตถุต้นแบบที่สามารถเข้าถึงได้จากอินสแตนซ์แรกที่สร้างโดยตัวสร้างจะไม่มีคุณสมบัติวัตถุต้นแบบในคำสั่ง IF
ฟังก์ชั่นบุคคล (ชื่ออายุ) {this.name = name; this.age = อายุ; if (typeof this.logname! = 'function') {person.prototype = {logName: function () {console.log (this.name); }, logage: function () {console.log (this.age); }}}};} var person1 = บุคคลใหม่ ('evansdiy', '22'); var person2 = บุคคลใหม่ ('amy', '21'); person2.logname (); // 'amy'person1.logname ();ควรสังเกตว่าแต่ละรุ่นมีสถานการณ์แอปพลิเคชันของตัวเองและไม่สำคัญว่าข้อดีและข้อเสีย
การวิเคราะห์รูปแบบต่าง ๆ ของการสร้างวัตถุใน JavaScript เป็นเนื้อหาทั้งหมดที่ฉันแบ่งปันกับคุณ ฉันหวังว่ามันจะให้ข้อมูลอ้างอิงและฉันหวังว่าคุณจะสนับสนุน wulin.com มากขึ้น