ต้นแบบคืออะไร
ประเภทฟังก์ชันมีต้นแบบคุณสมบัติซึ่งแปลโดยตรงเป็นต้นแบบ คุณสมบัตินี้เป็นตัวชี้ชี้ไปที่วัตถุซึ่งมีคุณสมบัติและวิธีการบางอย่างซึ่งจะถูกแชร์โดยทุกอินสแตนซ์ (วัตถุ) ที่สร้างโดยฟังก์ชันปัจจุบัน
จากสิ่งที่พูดก่อนหน้านี้คุณสามารถรับรหัสต่อไปนี้:
Function Person () {... } person.prototype = {Country: 'China', sayname: function () {... }}ขั้นแรกให้มีการสร้างบุคคลที่มีอินสแตนซ์ของประเภทฟังก์ชันจากนั้นต้นแบบวิธีการของบุคคลนั้นเป็นวัตถุและการประกาศชี้ไปที่วัตถุ คุณสมบัติและวิธีการในวัตถุนี้จะถูกแชร์โดยอินสแตนซ์ที่สร้างโดยฟังก์ชันบุคคลปัจจุบัน กล่าวคือ:
person1 = คนใหม่ (); person2 = คนใหม่ ();
person1 และ person2 ทั้งคู่ถูกสร้างขึ้นอีกครั้งผ่านอินสแตนซ์ประเภทฟังก์ชันบุคคล ทั้งคู่มีประเทศอสังหาริมทรัพย์ทั่วไปและวิธีการ Sayname เพราะพวกเขาทั้งหมดมีตัวชี้ (__proto__) ชี้ไปที่วัตถุโดยตรงที่ชี้ไปที่บุคคล prototype อย่างไรก็ตามโปรดทราบว่าตัวชี้ __proto__ ไม่ได้มาตรฐาน มันถูกกำหนดโดยเบราว์เซอร์เช่น Chrome และ Firefox ในความเป็นจริงคุณสมบัตินี้จะไม่ถูกใช้ แต่ใช้เป็นเพียงความเข้าใจของต้นแบบเท่านั้น:
เกี่ยวกับการใช้ต้นแบบและวิธีการอื่น ๆ เราจะพูดถึงเรื่องนี้โดยเฉพาะในภายหลัง
สร้างรูปแบบวัตถุ
จากนั้นมาดูวิธีการและรูปแบบทั่วไปสำหรับการสร้างวัตถุรวมถึงข้อดีและข้อเสียของพวกเขา
1. แบบจำลองโรงงาน
เช่นเดียวกับโรงงานกระบวนการสร้างวัตถุคอนกรีตนั้นเป็นนามธรรมและใช้ฟังก์ชั่นเพื่อห่อหุ้มรายละเอียดของการสร้างวัตถุที่มีอินเทอร์เฟซเฉพาะ โดยใช้ฟังก์ชั่นแทนการทำซ้ำบางส่วนรหัสมีดังนี้:
ฟังก์ชั่น createperson (ชื่อ, อายุ, งาน) {var o = วัตถุใหม่ (); o.name = ชื่อ; O.AGE = อายุ; o.job = โยบ; o.sayname = function () {Alert (this.name); - return o;} var person1 = createperson ("Jiangshui", "22", "วิศวกร");สิ่งนี้สร้างบุคคลและรูปแบบโรงงานแก้ปัญหาของการสร้างวัตถุที่คล้ายกันหลายครั้งซ้ำ ๆ แต่ไม่ได้แก้ปัญหาการจดจำวัตถุ มันเป็นเพียงการสร้างวัตถุเพียงอย่างเดียวและไม่ว่าวัตถุนี้จะถูกสร้างขึ้นจากเทมเพลตมนุษย์หรือเทมเพลตสัตว์มันเป็นไปไม่ได้ที่จะแยกแยะประเภทของวัตถุนี้
2. โหมดตัวสร้าง
สร้างตัวสร้างที่กำหนดเองเพื่อกำหนดคุณสมบัติและวิธีการของประเภทวัตถุที่กำหนดเอง
ฟังก์ชั่นบุคคล (ชื่อ, อายุ, งาน) {this.name = name; this.age = อายุ; this.job = jpb; this.sayName = function () {Alert (this.name); };}; var person1 = บุคคลใหม่ (... );3. ความแตกต่างระหว่างโหมดคอนสตรัคเตอร์และโหมดโรงงาน:
บุคคลเป็นวัตถุของประเภทฟังก์ชั่น หลังจากใหม่วัตถุจะยังคงถูกสร้างขึ้น อย่างไรก็ตามเนื่องจากวัตถุที่สร้างขึ้นใหม่นี้ถูกส่งผ่านในฟังก์ชันและกำหนดให้กับตัวชี้นี้เนื้อหาที่ส่งผ่านกลายเป็นคุณสมบัติหรือวิธีการของวัตถุที่สร้างขึ้นใหม่
นิสัยเริ่มต้นของตัวสร้างเป็นตัวพิมพ์ใหญ่ในตัวอักษรแรก การดำเนินการรหัสด้านบนต้องผ่านขั้นตอนต่อไปนี้:
ในอินสแตนซ์ที่สร้างขึ้นด้วยวิธีนี้พวกเขาทั้งหมดมีแอตทริบิวต์ตัวสร้างโดยเริ่มต้นชี้ไปที่ฟังก์ชั่นคอนสตรัคเตอร์ตัวอย่างเช่น:
การแจ้งเตือน (person1.constructor == บุคคล);
ดังนั้นการใช้รูปแบบตัวสร้างจึงมีความแตกต่างประเภทและอินสแตนซ์ของมันสามารถระบุได้ว่าเป็นประเภทเฉพาะ
นอกจากนี้ตัวสร้างเป็นฟังก์ชั่นธรรมดา เนื่องจากคุณต้องการตอบรับเพื่อรับวัตถุใหม่คุณใช้ใหม่เพื่อเรียกมัน ถ้าไม่การดำเนินการโดยตรงก็เป็นเหมือนฟังก์ชั่นปกติ ตัวอย่างเช่นหากคุณเรียกใช้ Person.SayName () ด้านบน Window.Name จะปรากฏขึ้นเนื่องจากฟังก์ชั่นถูกดำเนินการภายใต้หน้าต่างดังนั้นจุดนี้ไปยังหน้าต่าง
โหมดตัวสร้างก็มีข้อบกพร่องเช่นกัน วิธีการในโหมดตัวสร้างจะถูกสร้างขึ้นใหม่ในแต่ละอินสแตนซ์ดังนั้นฟังก์ชั่นที่มีชื่อเดียวกันในอินสแตนซ์ที่แตกต่างกันจะไม่เท่ากัน ตัวอย่างเช่น:
person1.SayName == person2.SayName; //เท็จ
กล่าวคือแต่ละอินสแตนซ์ของวัตถุแอตทริบิวต์และวิธีการที่สร้างโดยตัวสร้างนั้นไม่ซ้ำกันและถูกคัดลอก แอตทริบิวต์นั้นไม่ซ้ำกันเพราะนี่คือความแตกต่างระหว่างวัตถุ แต่หลายวิธีมีฟังก์ชั่นและรหัสเดียวกัน หากคุณคัดลอกซ้ำหลายครั้งคุณจะต้องเสียทรัพยากรอย่างเห็นได้ชัด
ดังนั้นเราสามารถใส่ฟังก์ชั่นออกไปข้างนอกแล้วชี้ไปที่ฟังก์ชั่นด้วยตัวชี้ในตัวสร้าง ในอินสแตนซ์ที่สร้างขึ้นวิธีการจัดเก็บตัวชี้ไปยังฟังก์ชั่นบางอย่างซึ่งหมายถึงฟังก์ชั่นที่ใช้ร่วมกัน:
ฟังก์ชั่นบุคคล (ชื่ออายุ) {this.name = name; this.age = อายุ; this.sayname = sayname;} ฟังก์ชั่น sayname () {alert (this.name);}อย่างไรก็ตามด้วยวิธีนี้ฟังก์ชั่นนี้กลายเป็นฟังก์ชั่นระดับโลกและไม่สัมพันธ์กับตัวสร้างบุคคลและไม่มีการห่อหุ้ม
ต่อไปโปรดมาที่โหมดต้นแบบ
โหมดต้นแบบ
ส่วนหนึ่งของพื้นฐานเกี่ยวกับต้นแบบได้รับการแนะนำก่อนหน้านี้ พูดง่ายๆว่าแต่ละฟังก์ชั่นมีแอตทริบิวต์ต้นแบบชี้ไปที่วัตถุ (วัตถุต้นแบบ) และคุณสมบัติหรือวิธีการบางอย่างสามารถวางไว้ในวัตถุนี้ จากนั้นอินสแตนซ์ที่สร้างโดยฟังก์ชั่นนี้จะมีแอตทริบิวต์ที่ผิดปกติ (__proto__) ชี้ไปที่ต้นแบบ
จากมุมมองนี้คุณควรจะสามารถเข้าใจได้ว่าคุณสมบัติและวิธีการที่สร้างโดยต้นแบบนั้นถูกใช้ร่วมกันโดยทุกกรณี
นี่เป็นเพียงการแก้ปัญหาของฟังก์ชั่นการแชร์ในโหมดคอนสตรัคเตอร์ข้างต้นและในตัวอย่าง ตัวอย่างเช่นรหัสต่อไปนี้:
ฟังก์ชันบุคคล () {.... } person.prototype.name = "Jiangshui"; person.prototype.sayname = function () {Alert (this.name);}; var person1 = บุคคลใหม่ (); person1.SayName (); // Jiangshuiหรือ
person.prototype = {constructor: บุคคล, ชื่อ: "Jiangshui", sayname: function () {Alert (this.name); -วิธีที่สองครอบคลุมวัตถุต้นแบบทั้งหมดดังนั้นคุณต้องระบุคุณสมบัติตัวสร้างด้วยตนเองชี้ไปที่ฟังก์ชันตัวสร้างมิฉะนั้นจะชี้ไปที่วัตถุ
มาแยกแยะความสัมพันธ์ของพวกเขากันเถอะ:
ใช้ isprototypeof () เพื่อกำหนดความสัมพันธ์ระหว่างวัตถุ ตัวอย่างเช่น:
person.prototype.isprototypeof (person1);
เมื่อรหัสอ่านคุณสมบัติบางอย่างของวัตถุการค้นหาจะดำเนินการ เริ่มต้นด้วยวัตถุปัจจุบันและหากไม่ได้ค้นหาวัตถุต้นแบบที่ชี้ไปที่ตัวชี้โดยไม่ต้องค้นหาตัวสร้าง สามารถเข้าถึงอินสแตนซ์ของวัตถุได้ แต่ไม่สามารถแทนที่ค่าของวัตถุต้นแบบได้ หากแอตทริบิวต์ที่มีชื่อเดียวกันกับวัตถุต้นแบบถูกตั้งค่าในอินสแตนซ์กระบวนการค้นหาจะสิ้นสุดในอินสแตนซ์โดยไม่ต้องเข้าถึงวัตถุต้นแบบดังนั้นวัตถุประสงค์ของการทับเขียนจะทำได้ ดังนั้นแม้ว่าคุณสมบัตินี้จะถูกตั้งค่าเป็น null แต่ก็หมายความว่าคุณสมบัติที่มีอยู่แล้วในอินสแตนซ์และคุณสมบัติจะไม่ถูกยกเลิกเพื่อให้สามารถเข้าถึงคุณสมบัติที่สอดคล้องกันของต้นแบบได้
ดังนั้นคุณต้องใช้ตัวดำเนินการลบเพื่อลบแอตทริบิวต์อินสแตนซ์อย่างสมบูรณ์เพื่อให้สามารถทบทวนต้นแบบได้อีกครั้ง
ต้นแบบเป็นแบบไดนามิกและการดัดแปลงใด ๆ ที่ทำกับวัตถุต้นแบบสามารถสะท้อนได้ทันทีจากอินสแตนซ์ เหตุผลคือความสัมพันธ์แบบลิงก์แบบหลวมระหว่างอินสแตนซ์และต้นแบบ ทุกครั้งที่มีการเรียกใช้วิธีการของอินสแตนซ์การสืบค้นจะดำเนินการ หากต้นแบบเปลี่ยนแปลงผลลัพธ์การสืบค้นก็จะเปลี่ยนไปเช่นกัน
หลังจากทำความเข้าใจต้นแบบแล้วเรายังสามารถเพิ่มวิธีการใหม่หรือแอตทริบิวต์ให้กับวัตถุดั้งเดิม ประเภทการอ้างอิงดั้งเดิมเช่นวัตถุอาร์เรย์สตริง ฯลฯ มีความคล้ายคลึงกับตัวสร้างด้านบน เราสามารถใช้ต้นแบบเพื่อขยายวิธีการของพวกเขา ตัวอย่างเช่น:
string.prototype.startswith = function (text) {return this.indexof (text) == 0;}; var msg = "Hello World"; msg.startswith ("Hello");รหัสนี้เพิ่มวิธี startswith ลงในสตริงประเภทอ้างอิงดั้งเดิมซึ่งจะส่งผ่านในพารามิเตอร์เพื่อดูว่าสตริงที่จะทดสอบเริ่มต้นด้วยพารามิเตอร์หรือไม่ เนื่องจากลักษณะแบบไดนามิกของต้นแบบตัวแปรทั้งหมดของประเภทสตริงจึงได้รับวิธีนี้โดยการดำเนินการ
อย่างไรก็ตามวิธีนี้ไม่แนะนำให้ใช้ หากคุณใช้รหัสมากเกินไปและมากเกินไปมันจะนำไปสู่ความยากลำบากในการบำรุงรักษาความสับสนในรหัส ฯลฯ โดยทั่วไปการพูดประเภทการอ้างอิงดั้งเดิมจะได้รับการสืบทอดก่อนจากนั้นสร้างขึ้นในประเภทที่กำหนดเองใหม่ เกี่ยวกับมรดกเราจะสรุปในภายหลัง
รูปแบบต้นแบบไม่ได้มีอำนาจทุกอย่างเช่นกัน คุณลักษณะและวิธีการทั้งหมดในต้นแบบจะถูกใช้ร่วมกันโดยทุกกรณีดังนั้นจึงเหมาะอย่างยิ่งสำหรับฟังก์ชั่นและฟังก์ชั่นอื่น ๆ แต่สำหรับคุณลักษณะที่มีประเภทอ้างอิงความขัดแย้งบางอย่างจะเกิดขึ้น ตัวอย่างเช่น:
ฟังก์ชันบุคคล () {} person.prototype = {constructor: บุคคล, เพื่อน: ["greg", "jack"]}; var person1 = บุคคลใหม่ (); var person2 = บุคคลใหม่ (); person1.friends.push ("tom"); console.log (person2.friends);คุณจะเห็นในคอนโซลว่ามีทอมพิเศษสำหรับเพื่อน Person2 ซึ่งไม่ใช่สิ่งที่ฉันต้องการ แต่เมื่อกำหนดเพื่อนของเขาให้กับบุคคล 1 มันจะส่งผลกระทบต่อตัวอย่างบุคคล 2
ดังนั้นเราจำเป็นต้องใช้ร่วมกับรูปแบบต้นแบบและรูปแบบตัวสร้าง
ใช้โหมดตัวสร้างและโหมดต้นแบบร่วมกัน
นี่คือรูปแบบที่ใช้กันมากที่สุด ตัวสร้างถูกใช้เพื่อกำหนดคุณสมบัติอินสแตนซ์และปรับแต่งโดยผ่านพารามิเตอร์ ต้นแบบใช้เพื่อกำหนดวิธีการหรือคุณลักษณะที่ต้องใช้การแบ่งปันระหว่างทุกกรณี ด้วยวิธีนี้การปรับแต่งจะประสบความสำเร็จการแบ่งปันจะช่วยให้มั่นใจได้และหลีกเลี่ยงปัญหา
ฟังก์ชั่นบุคคล (ชื่อ, อายุ, งาน) {this.name = name; this.age = อายุ; this.job = งาน; this.friends = ["greg", "jack"];} person.prototype = {constructor: person, sayname: function () {alert (this.name); }}; var Jiangshui = คนใหม่ ("Jiangshui", "22", "วิศวกร");ตัวอย่างการใช้งานจริง
ตกลงที่นี่คุณอาจเข้าใจว่าต้นแบบคืออะไรและจะสร้างวัตถุได้อย่างไร แต่การใช้สิ่งเหล่านี้คืออะไร? ที่จริงแล้วงานก่อนหน้าของฉันคือการเขียนโค้ดโดยใช้ jQuery และฉันไม่สามารถใช้การห่อหุ้มแล้วสร้างวัตถุเพื่อใช้งานฟังก์ชั่น ฯลฯ ดังนั้นการใช้สิ่งเหล่านี้คืออะไร?
วิธีการพัฒนานี้ส่วนใหญ่ใช้สำหรับการพัฒนาแบบแยกส่วนและการประกอบ ตัวอย่างเช่นฟังก์ชั่นป๊อปอัพที่คุณใช้บ่อย ๆ แน่นอนว่าคุณสามารถวางและคัดลอกรหัสป๊อปอัพทุกครั้งจากนั้นแก้ไขและใช้ในโครงการ ตัวเลือกที่ดีกว่าคือการห่อหุ้มรหัสฟังก์ชันป๊อปอัพของคุณอย่างเป็นนามธรรมลงในส่วนประกอบดังกล่าวดังนั้นเมื่อคุณต้องการใช้ป๊อปอัปคุณจะต้องส่งพารามิเตอร์เพื่อสร้างอินสแตนซ์ป๊อปอัพและคุณสามารถเรียกได้
วัตถุต้นแบบและห่วงโซ่ต้นแบบ
ในจาวาสคริปต์ทุกอย่างเป็นวัตถุ แต่ก็มีความแตกต่างในวัตถุ มันสามารถแบ่งออกเป็นสองประเภท ได้แก่ : วัตถุธรรมดา (วัตถุ) และวัตถุฟังก์ชัน (ฟังก์ชั่น)
โดยทั่วไปวัตถุที่สร้างขึ้นผ่านฟังก์ชั่นใหม่คือวัตถุฟังก์ชันและวัตถุอื่น ๆ เป็นวัตถุธรรมดา
ยกตัวอย่าง:
ฟังก์ชั่น f1 () {// toDo} var f2 = function () {// toDo}; var f3 = ฟังก์ชันใหม่ ('x', 'console.log (x)'); var o1 = {}; var o2 = วัตถุใหม่ (); var o3 = ใหม่ f1 (); console.log (typeof f1, // function typeof f2, // function typeof f3, // function typeof o1, // object typeof o2, // type off Object On // Object); >> ฟังก์ชันฟังก์ชันวัตถุวัตถุวัตถุวัตถุวัตถุวัตถุวัตถุวัตถุวัตถุวัตถุวัตถุวัตถุวัตถุวัตถุวัตถุวัตถุวัตถุวัตถุวัตถุวัตถุวัตถุวัตถุวัตถุวัตถุวัตถุวัตถุวัตถุวัตถุวัตถุวัตถุวัตถุวัตถุวัตถุวัตถุวัตถุวัตถุวัตถุวัตถุวัตถุวัตถุวัตถุวัตถุวัตถุวัตถุวัตถุวัตถุวัตถุวัตถุวัตถุวัตถุวัตถุวัตถุวัตถุวัตถุF1 เป็นของการประกาศฟังก์ชั่น วิธีที่พบบ่อยที่สุดในการกำหนดฟังก์ชั่นคือ F2 เป็นฟังก์ชันที่ไม่ระบุชื่อ กำหนดฟังก์ชั่นที่ไม่ระบุชื่อนี้ให้กับ F2 ซึ่งเป็นของนิพจน์ฟังก์ชั่น F3 ไม่ธรรมดา แต่ก็เป็นวัตถุฟังก์ชัน
ฟังก์ชั่นเป็นวัตถุที่มาพร้อมกับ JS เมื่อสร้าง F1 และ F2 JS จะสร้างวัตถุเหล่านี้โดยอัตโนมัติผ่านฟังก์ชั่นใหม่ () ดังนั้นวัตถุทั้งสามนี้จึงถูกสร้างขึ้นผ่านฟังก์ชั่นใหม่ ()
มีสองวิธีในการสร้างวัตถุใน JavaScript: ตัวอักษรวัตถุและนิพจน์ใหม่ การสร้าง O1 และ O2 นั้นสอดคล้องกับสองวิธีนี้ มามุ่งเน้นไปที่ O3 หากคุณใช้แนวคิดของ Java และ C# เพื่อทำความเข้าใจ O3 เป็นวัตถุอินสแตนซ์ของ F1 และ O3 และ F1 เป็นประเภทเดียวกัน อย่างน้อยฉันก็คิดอย่างนั้นมาก่อน แต่ไม่ใช่ ...
แล้วคุณเข้าใจได้อย่างไร? มันง่ายมาก ดูว่า O3 ถูกสร้างขึ้นผ่านฟังก์ชั่นใหม่หรือไม่ เห็นได้ชัดว่าไม่ เนื่องจากไม่ใช่วัตถุฟังก์ชันจึงเป็นวัตถุธรรมดา
หลังจากเข้าใจวัตถุฟังก์ชั่นและวัตถุธรรมดาอย่างง่ายแล้วให้เรียนรู้เกี่ยวกับต้นแบบและเครือข่ายต้นแบบใน JavaScript:
ใน JS เมื่อใดก็ตามที่มีการสร้างวัตถุฟังก์ชัน F1 คุณสมบัติบางอย่างจะถูกสร้างขึ้นในวัตถุรวมถึงต้นแบบและ __proto__ ต้นแบบเป็นวัตถุต้นแบบซึ่งบันทึกคุณสมบัติและวิธีการบางอย่างของ F1
ควรสังเกตว่าต้นแบบนั้นมองไม่เห็น F1 นั่นคือ F1 จะไม่มองหาคุณสมบัติและวิธีการในต้นแบบ
ฟังก์ชั่น f () {} f.prototype.foo = "abc"; console.log (f.foo); // ไม่ได้กำหนดดังนั้นการใช้ต้นแบบคืออะไร? ในความเป็นจริงฟังก์ชั่นหลักของต้นแบบคือการสืบทอด ในแง่ของ Layman คุณสมบัติและวิธีการที่กำหนดไว้ในต้นแบบนั้นถูกทิ้งให้อยู่ใน "ลูกหลาน" ของพวกเขาดังนั้นคลาสย่อยสามารถเข้าถึงคุณสมบัติและวิธีการในต้นแบบได้อย่างเต็มที่
หากต้องการทราบว่า F1 ออกต้นแบบสำหรับ "ลูกหลาน" อย่างไรเราต้องเข้าใจห่วงโซ่ต้นแบบใน JS ในเวลานี้ __proto__ ใน JS ได้เข้าสู่ตลาด ผู้ชายคนนี้แปลกและซ่อนเร้นมากดังนั้นคุณมักจะไม่เห็น แต่มันมีอยู่ทั้งในวัตถุธรรมดาและวัตถุฟังก์ชั่น ฟังก์ชั่นของมันคือการบันทึกวัตถุต้นแบบของคลาสแม่ เมื่อ JS สร้างวัตถุผ่านนิพจน์ใหม่มันมักจะกำหนดต้นแบบของคลาสแม่ให้กับแอตทริบิวต์ __proto__ ของวัตถุใหม่ซึ่งเป็นรุ่นของการสืบทอด ...
ฟังก์ชั่น f () {} f.prototype.foo = "abc"; var obj = new f (); console.log (obj.foo); // ABCตอนนี้เรารู้แล้วว่า __proto__ ใน OBJ บันทึกต้นแบบของ F ดังนั้นสิ่งที่บันทึกไว้ใน __proto__ ในต้นแบบของ F คืออะไร? ดูภาพต่อไปนี้:
ดังที่แสดงในรูปวัตถุ prototype ที่เก็บไว้ใน __proto__ ของ f.prototype และยังมี __proto__ ในวัตถุวัตถุ prototype และจากผลลัพธ์เอาท์พุท Object.prototype .__ Proto__ เป็นโมฆะ ดังที่แสดงในรูปด้านล่าง:
หลังจากวัตถุ OBJ มีห่วงโซ่ต้นแบบดังกล่าวเมื่อ OBJ.FOO ถูกดำเนินการ OBJ จะพบว่ามีแอตทริบิวต์หรือไม่ แต่จะไม่พบต้นแบบของตัวเอง เมื่อไม่พบฟู OBJ จะค้นหาตามห่วงโซ่ต้นแบบ ...
ในตัวอย่างข้างต้นเรากำหนดแอตทริบิวต์ FOO บนต้นแบบของ F จากนั้น OBJ จะพบคุณลักษณะนี้ในห่วงโซ่ต้นแบบและดำเนินการ