โปรดลืมความรู้เชิงวัตถุทั้งหมดที่คุณเรียนรู้มาก่อน เพียงแค่พิจารณาสถานการณ์การแข่งรถที่นี่ ใช่มันคือการแข่งรถ
เมื่อเร็ว ๆ นี้ฉันกำลังดู 24 ชั่วโมงของ Le Mans ซึ่งเป็นกิจกรรมยอดนิยมในฝรั่งเศส รถที่เร็วที่สุดเรียกว่าต้นแบบ Le Mans แม้ว่ารถยนต์เหล่านี้จะผลิตโดยผู้ผลิตเช่น "Audi" หรือ "Peugeot" แต่พวกเขาไม่ใช่รถประเภทที่คุณเห็นบนถนนหรือบนทางหลวง พวกเขาถูกสร้างขึ้นโดยเฉพาะสำหรับกิจกรรมความอดทนความเร็วสูง
ผู้ผลิตลงทุนเงินจำนวนมากในการพัฒนาออกแบบและผลิตรถยนต์ต้นแบบเหล่านี้และวิศวกรมักจะพยายามทำให้โครงการนี้รุนแรง พวกเขาทำการทดลองต่าง ๆ เกี่ยวกับโลหะผสมเชื้อเพลิงชีวภาพเทคโนโลยีเบรกองค์ประกอบผสมและลักษณะความปลอดภัยของยาง เมื่อเวลาผ่านไปเทคนิคบางอย่างในการทดลองเหล่านี้ได้รับการปรับปรุงซ้ำ ๆ และป้อนสายผลิตภัณฑ์หลักของยานพาหนะ เทคโนโลยีบางอย่างในยานพาหนะที่คุณขับอาจเปิดตัวในต้นแบบการแข่งรถ
นอกจากนี้คุณยังสามารถพูดได้ว่ายานพาหนะกระแสหลักเหล่านี้ สืบทอด ต้นแบบ ทางเทคนิคจากการแข่งรถ
จนถึงตอนนี้เรามีพื้นฐานสำหรับการพูดคุยเรื่องต้นแบบและปัญหาการสืบทอดในจาวาสคริปต์ แม้ว่ามันจะไม่ดีเท่ารูปแบบการสืบทอดคลาสสิกที่คุณรู้จักใน C ++, Java หรือ C#แต่มันก็มีประสิทธิภาพและอาจยืดหยุ่นมากขึ้น
JavaScript เต็มไปด้วยวัตถุซึ่งหมายถึงวัตถุในความหมายดั้งเดิมนั่นคือ "เอนทิตีเดียวที่มีสถานะและพฤติกรรม" ตัวอย่างเช่นอาร์เรย์ใน JavaScript เป็นวัตถุที่มีค่าหลายค่าและมีวิธีการแบบ push, reverse และ POP
var myarray = [1, 2]; myarray.push (3); myarray.Reverse (); myarray.pop (); var length = myarray.length;
ตอนนี้คำถามคือการผลักดันมาจากไหน? ภาษาคงที่ที่เรากล่าวถึงก่อนหน้านี้ใช้ "คลาสไวยากรณ์คลาส" เพื่อกำหนดโครงสร้างของวัตถุ แต่จาวาสคริปต์เป็นภาษาที่ไม่มี "ไวยากรณ์คลาส" และไม่สามารถกำหนดวัตถุอาร์เรย์แต่ละวัตถุโดยใช้ไวยากรณ์ของอาร์เรย์ "คลาส" และเนื่องจาก JavaScript เป็นภาษาแบบไดนามิกเราจึงสามารถวางวิธีการบนวัตถุตามที่เราต้องการ ตัวอย่างเช่นรหัสต่อไปนี้กำหนดวัตถุจุดที่ใช้เพื่อแสดงจุดในพื้นที่สองมิติและยังกำหนดวิธีการเพิ่ม
var point = {x: 10, y: 5, เพิ่ม: ฟังก์ชั่น (อื่น ๆ ) {this.x += otherpoint.x; this.y += otherpoint.y; -อย่างไรก็ตามการปฏิบัติข้างต้นไม่สามารถปรับขนาดได้มากนัก เราจำเป็นต้องตรวจสอบให้แน่ใจว่าวัตถุแต่ละจุดมีวิธีการเพิ่มและเราต้องการให้วัตถุจุดทั้งหมดแบ่งปันการใช้วิธีการเพิ่มเดียวกันแทนที่จะเพิ่มวิธีนี้ด้วยตนเองในแต่ละจุดจุด นี่คือที่ที่ต้นแบบเข้ามาเล่น
ในจาวาสคริปต์แต่ละวัตถุยังคงซ่อนอยู่ - การอ้างอิงไปยังวัตถุอื่นหรือที่เรียกว่าต้นแบบ อาร์เรย์ที่เราสร้างก่อนอ้างอิงวัตถุต้นแบบและวัตถุจุดที่เราสร้างขึ้นเอง ดังที่ได้กล่าวไว้ข้างต้นการอ้างอิงต้นแบบจะถูกซ่อนอยู่ แต่ยังมีการใช้งาน ECMASCript (ชื่ออย่างเป็นทางการของ JavaScript) ที่สามารถเข้าถึงการอ้างอิงต้นแบบนี้ผ่านแอตทริบิวต์ __Proto__ ของวัตถุ (เช่น Google Chrome) แนวคิดเราสามารถปฏิบัติต่อวัตถุเป็นความสัมพันธ์ที่คล้ายคลึงกับที่แสดงในรูปที่ 1 รูปแบบ
รูปที่ 1
เมื่อมองไปข้างหน้านักพัฒนาจะสามารถใช้ object.getPrototypeof ฟังก์ชั่นแทนแอตทริบิวต์ __proto__ เพื่อรับการอ้างอิงไปยังต้นแบบวัตถุ ในช่วงเวลาของการเขียนบทความนี้ฟังก์ชั่น getPrototypeof สามารถใช้ในเบราว์เซอร์ Google Chrome, Firefox และ IE9 ได้แล้ว เบราว์เซอร์เพิ่มเติมจะใช้คุณสมบัตินี้ในอนาคตเพราะเป็นส่วนหนึ่งของมาตรฐาน ECMASCRIPT อยู่แล้ว เราสามารถใช้รหัสต่อไปนี้เพื่อพิสูจน์ว่าวัตถุ MyArray และ DOT ที่เราสร้างขึ้นอ้างถึงวัตถุต้นแบบสองวัตถุที่แตกต่างกัน
สำหรับส่วนที่เหลือของบทความนี้ฉันจะใช้ __proto__ และ object.getPrototypeof ฟังก์ชั่นส่วนใหญ่เป็นเพราะ __proto__ ง่ายต่อการระบุในกราฟและประโยค ควรจำไว้ว่ามัน (__proto__) ไม่ใช่มาตรฐานและฟังก์ชั่น Object.getPrototypeof เป็นวิธีที่แนะนำในการดูต้นแบบวัตถุ
อะไรทำให้ต้นแบบพิเศษมาก?
เรายังไม่ได้ตอบคำถามนี้: การกดในอาร์เรย์มาจากไหน? คำตอบคือ: มันมาจากวัตถุต้นแบบ MyArray รูปที่ 2 เป็นภาพหน้าจอของตัวดีบักสคริปต์ใน Chrome เราได้เรียกว่า Object.getPrototypeof เพื่อดูวัตถุต้นแบบของ MyArray
รูปที่ 2
โปรดทราบว่ามีวิธีการมากมายในวัตถุต้นแบบของ MyArray รวมถึงวิธีการที่เรียกว่า push, POP และวิธีการย้อนกลับในตัวอย่างรหัส ดังนั้นวิธีการผลักดันจึงรวมถึงวัตถุต้นแบบ แต่วิธี MyArray อ้างถึงมันอย่างไร?
myarray.push (3);
ขั้นตอนแรกในการทำความเข้าใจวิธีการทำงานคือการตระหนักว่าต้นแบบไม่พิเศษ ต้นแบบเป็นเพียงวัตถุปกติ คุณสามารถเพิ่มวิธีการคุณสมบัติให้กับต้นแบบและปฏิบัติต่อพวกเขาเป็นวัตถุ JavaScript อื่น ๆ อย่างไรก็ตามการใช้คำแถลงของ "หมู" ในนวนิยายของจอร์จออร์เวลล์ "ฟาร์มสัตว์" - วัตถุทั้งหมดควรเท่ากัน แต่วัตถุบางอย่าง (ผู้ที่ปฏิบัติตามกฎ) มีความเท่าเทียมกันมากกว่าสิ่งอื่น ๆ
วัตถุต้นแบบใน JavaScript นั้นพิเศษเพราะพวกเขาปฏิบัติตามกฎต่อไปนี้ เมื่อเราบอก JavaScript ว่าเราต้องการเรียกใช้วิธีการพุชของวัตถุหรืออ่านคุณสมบัติ X ของวัตถุรันไทม์จะค้นหาวัตถุตัวเองก่อน หากรันไทม์ไม่สามารถหาสิ่งที่ต้องการได้จะเป็นไปตามการอ้างอิง __Proto__ และต้นแบบวัตถุเพื่อค้นหาสมาชิก เมื่อเราเรียกวิธีการผลักดันของ MyArray JavaScript ไม่พบวิธีการผลักดันบนวัตถุ MyArray แต่บนวัตถุต้นแบบของ MyArray ดังนั้น JavaScript จึงเรียกวิธีนี้ (ดูรูปที่ 3)
รูปที่ 3
พฤติกรรมที่อธิบายไว้ข้างต้นหมายถึงวัตถุเองที่สืบทอดวิธีการหรือคุณสมบัติใด ๆ บนต้นแบบ ใน JavaScript การสืบทอดจะเกิดขึ้นได้จริงโดยไม่ต้องใช้ไวยากรณ์คลาส เช่นเดียวกับรถยนต์ที่สืบทอดเทคโนโลยีที่สอดคล้องกันจากต้นแบบการแข่งรถวัตถุ JavaScript ยังสามารถสืบทอดคุณสมบัติการทำงานได้จากวัตถุต้นแบบ
รูปที่ 3 ยังแสดงให้เห็นว่าวัตถุอาเรย์แต่ละชิ้นยังสามารถรักษาสถานะและสมาชิกของตนเองได้ เมื่อร้องขอแอตทริบิวต์ความยาวของ MyArray JavaScript จะได้รับค่าของแอตทริบิวต์ความยาวใน MyArray โดยไม่ต้องอ่านค่าที่สอดคล้องกันในต้นแบบ เราสามารถ "เขียนทับ" วิธีการผลักดันโดยการเพิ่มวิธีการเช่นกดไปยังวัตถุ สิ่งนี้จะซ่อนการใช้วิธีการผลักดันอย่างมีประสิทธิภาพในต้นแบบ
ความมหัศจรรย์ที่แท้จริงของต้นแบบใน JavaScript คือวิธีที่วัตถุหลายชิ้นยังคงอ้างอิงถึงวัตถุต้นแบบเดียวกัน ตัวอย่างเช่นถ้าเราสร้างสองอาร์เรย์เช่นนี้:
var myarray = [1, 2]; var yourarray = [4, 5, 6];
จากนั้นอาร์เรย์ทั้งสองนี้จะแบ่งปันวัตถุต้นแบบเดียวกันและรหัสต่อไปนี้จะประเมินเป็นจริง:
Object.getPrototypeof (myArray) === Object.getPrototypeof (YourArray);
หากเราอ้างถึงวิธีการพุชในวัตถุอาร์เรย์สองชิ้น JavaScript จะมองหาวิธีการผลักดันที่ใช้ร่วมกันบนต้นแบบ
รูปที่ 4
วัตถุต้นแบบใน JavaScript มีฟังก์ชั่นการสืบทอดและในเวลาเดียวกันการแบ่งปันวิธีนี้จะถูกนำมาใช้ ต้นแบบก็ถูกล่ามโซ่ กล่าวอีกนัยหนึ่งเนื่องจากวัตถุต้นแบบเป็นเพียงวัตถุวัตถุต้นแบบหนึ่งสามารถเก็บรักษาไว้ในการอ้างอิงไปยังวัตถุต้นแบบอื่น หากคุณทบทวนรูปที่ 2 คุณจะเห็นได้ว่าคุณสมบัติ __proto__ ของต้นแบบเป็นค่าที่ไม่ใช่ Null ที่ชี้ไปที่ต้นแบบอื่น เมื่อจาวาสคริปต์มองหาสมาชิกเช่นวิธีการพุชมันจะตรวจสอบแต่ละวัตถุตามห่วงโซ่อ้างอิงต้นแบบจนกว่าจะพบหรือถึงจุดสิ้นสุดของห่วงโซ่ต้นแบบ ห่วงโซ่ต้นแบบเปิดเส้นทางที่ยืดหยุ่นสำหรับการสืบทอดและการแบ่งปัน
คำถามต่อไปที่คุณอาจถามคือ: ฉันจะตั้งค่าการอ้างอิงต้นแบบไปยังวัตถุที่กำหนดเองเหล่านั้นได้อย่างไร ตัวอย่างเช่นวัตถุจุดที่ใช้ก่อนหน้านี้ฉันจะเพิ่มวิธีการเพิ่มลงในวัตถุต้นแบบและสืบทอดวิธีการจากวัตถุหลายจุดได้อย่างไร ก่อนที่จะตอบคำถามนี้เราต้องดูฟังก์ชั่น
ฟังก์ชั่นในจาวาสคริปต์ก็เป็นวัตถุ คำแถลงดังกล่าวนำผลลัพธ์ที่สำคัญหลายประการและเราจะไม่ครอบคลุมทุกเรื่องในบทความนี้ ในหมู่พวกเขาความสามารถในการกำหนดฟังก์ชั่นให้กับตัวแปรและผ่านฟังก์ชั่นเป็นพารามิเตอร์ไปยังฟังก์ชั่นอื่นถือเป็นกระบวนทัศน์พื้นฐานของนิพจน์การเขียนโปรแกรม JavaScript ที่ทันสมัย
สิ่งที่เราต้องให้ความสนใจคือฟังก์ชั่นนั้นเป็นวัตถุดังนั้นฟังก์ชั่นสามารถมีวิธีการคุณสมบัติและการอ้างอิงวัตถุต้นแบบของตัวเอง มาพูดคุยเกี่ยวกับความหมายของรหัสต่อไปนี้
// สิ่งนี้จะส่งคืนจริง: typeof (array) === "ฟังก์ชั่น" // นิพจน์ดังกล่าวยัง: object.getPrototypeof (อาร์เรย์) === Object.getPrototypeof (ฟังก์ชัน () {}) // นิพจน์ดังกล่าวเหมือนกัน: array.prototype! = nullบรรทัดแรกในรหัสพิสูจน์ว่าอาร์เรย์ใน JavaScript เป็นฟังก์ชัน เราจะดูวิธีเรียกใช้ฟังก์ชันอาร์เรย์เพื่อสร้างวัตถุอาร์เรย์ใหม่ บรรทัดถัดไปของรหัสพิสูจน์ว่าวัตถุอาร์เรย์ใช้ต้นแบบเดียวกันกับวัตถุฟังก์ชั่นอื่น ๆ เช่นเดียวกับที่เราเห็นว่าต้นแบบเดียวกันนี้ถูกแชร์ระหว่างวัตถุอาร์เรย์ บรรทัดสุดท้ายของรหัสพิสูจน์ว่าฟังก์ชั่นอาร์เรย์มีคุณสมบัติต้นแบบและคุณสมบัติของต้นแบบนี้ชี้ไปที่วัตถุที่ถูกต้อง คุณสมบัติต้นแบบนี้มีความสำคัญมาก
แต่ละวัตถุฟังก์ชันใน JavaScript มีคุณสมบัติต้นแบบ อย่า สับสนแอตทริบิวต์ __Proto__ ของคุณสมบัติต้นแบบนี้ พวกเขามีจุดประสงค์ที่แตกต่างกันและไม่ชี้ไปที่วัตถุเดียวกัน
// ส่งคืน trueObject.getPrototypeof (อาร์เรย์)! = array.prototype
Array .__ Proto__ จัดเตรียมต้นแบบอาร์เรย์ โปรดถือว่าเป็นวัตถุที่สืบทอดมาจากฟังก์ชั่นอาร์เรย์
array.protoype จัดเตรียมวัตถุต้นแบบสำหรับอาร์เรย์ทั้งหมด กล่าวคือมันให้วัตถุต้นแบบของวัตถุอาเรย์เช่น MyArray และยังมีวิธีการที่อาร์เรย์ทั้งหมดจะสืบทอด เราสามารถเขียนโค้ดเพื่อพิสูจน์ความจริงนี้ได้
// truearray.prototype == object.getPrototypeof (myarray) // นอกจากนี้ยังเป็น truearray.prototype == Object.getPrototypeof (YourArray);
นอกจากนี้เรายังสามารถใช้ความรู้ใหม่นี้เพื่อทาสีไดอะแกรมก่อนหน้า
รูปที่ 5
จากสิ่งที่คุณรู้ลองจินตนาการถึงกระบวนการสร้างวัตถุใหม่และทำให้วัตถุใหม่ทำงานเหมือนอาร์เรย์ วิธีหนึ่งคือใช้รหัสต่อไปนี้
// สร้างวัตถุว่างเปล่าใหม่ var o = {}; // สืบทอดมาจากต้นแบบเดียวกันวัตถุอาร์เรย์ o .__ proto__ = array.prototype; // ตอนนี้เราสามารถเรียกวิธีการใด ๆ ของอาร์เรย์ ... O.Push (3);แม้ว่ารหัสนี้จะน่าสนใจและใช้งานได้ แต่ปัญหาก็คือสภาพแวดล้อม JavaScript ทุกตัวไม่สนับสนุนคุณสมบัติวัตถุ __Proto__ ที่เขียนได้ โชคดีที่ JavaScript มีกลไกมาตรฐานสำหรับการสร้างวัตถุ มันต้องการเพียงหนึ่งตัวดำเนินการเพื่อสร้างวัตถุใหม่และตั้งค่าการอ้างอิง __proto__ ของวัตถุใหม่นั่นคือตัวดำเนินการ "ใหม่"
var o = new Array (); o.push (3);
ตัวดำเนินการใหม่ใน JavaScript มีสามงานพื้นฐาน ก่อนอื่นมันสร้างวัตถุเปล่าใหม่ ถัดไปมันจะตั้งค่าคุณสมบัติ __proto__ ของวัตถุใหม่เพื่อให้ตรงกับคุณสมบัติต้นแบบของฟังก์ชันที่เรียกว่า ในที่สุดผู้ประกอบการเรียกฟังก์ชั่นผ่านวัตถุใหม่เป็นข้อมูลอ้างอิง "นี้" หากคุณต้องการขยายรหัสสองบรรทัดสุดท้ายมันจะกลายเป็นสถานการณ์ต่อไปนี้:
var o = {}; o .__ proto__ = array.prototype; array.call (o); o.push (3);วิธีการโทรของฟังก์ชั่นช่วยให้คุณสามารถระบุวัตถุที่อ้างอิงโดย "นี้" ภายในฟังก์ชั่นเมื่อเรียกใช้ฟังก์ชัน แน่นอนผู้เขียนฟังก์ชั่นจำเป็นต้องใช้ฟังก์ชั่นดังกล่าวในกรณีนี้ เมื่อผู้เขียนสร้างฟังก์ชั่นดังกล่าวมันสามารถเรียกได้ว่าเป็นตัวสร้าง
ตัวสร้าง
ตัวสร้างนั้นเหมือนกับฟังก์ชั่นธรรมดา แต่มีคุณสมบัติพิเศษสองประการต่อไปนี้
อาร์เรย์เป็นตัวอย่างของตัวสร้าง ฟังก์ชั่นอาร์เรย์จะต้องใช้กับผู้ให้บริการใหม่และจดหมายเริ่มต้นของอาร์เรย์เริ่มต้น JavaScript มีอาร์เรย์เป็นฟังก์ชั่นในตัวและทุกคนสามารถเขียนตัวสร้างของตัวเองได้ ในความเป็นจริงในที่สุดเราก็สามารถเขียนคอนสตรัคเตอร์สำหรับวัตถุจุดที่สร้างขึ้นก่อนหน้านี้
var point = function (x, y) {this.x = x; this.y = y; this.add = function (อื่น ๆ ) {this.x += otherpoint.x; this.y += otherpoint.y; }} var p1 = จุดใหม่ (3, 4); var p2 = จุดใหม่ (8, 6); p1.add (p2);ในรหัสข้างต้นเราใช้ตัวดำเนินการใหม่และฟังก์ชันจุดเพื่อสร้างวัตถุจุดซึ่งมีแอตทริบิวต์ x และ y และวิธีการเพิ่ม คุณสามารถจินตนาการผลลัพธ์สุดท้ายดังแสดงในรูปที่ 6
รูปที่ 6
ปัญหาตอนนี้คือยังมีวิธีเพิ่มแยกต่างหากในแต่ละวัตถุจุดของเรา การใช้ต้นแบบและการสืบทอดที่เราได้เรียนรู้เราค่อนข้างจะถ่ายโอนวิธีการเพิ่มของวัตถุจุดจากแต่ละจุดอินสแตนซ์ไปยังจุด prototype เพื่อให้บรรลุผลของการสืบทอดวิธีการเพิ่มสิ่งที่เราต้องทำคือการปรับเปลี่ยนวัตถุ point.prototype
var point = function (x, y) {this.x = x; this.y = y;} point.prototype.add = function (otherpoint) {this.x += otherpoint.x; this.y += otherpoint.y;} var p1 = จุดใหม่ (3, 4); var p2 = จุดใหม่ (8, 6); p1.add (p2);ภารกิจเสร็จสิ้นแล้ว! เราเพิ่งเสร็จสิ้นโหมดการสืบทอดของต้นแบบใน JavaScript!
รูปที่ 7
สรุป
ฉันหวังว่าบทความนี้จะช่วยให้คุณเปิดเผยความลึกลับของแนวคิดต้นแบบ JavaScript สิ่งที่ฉันเห็นในตอนแรกคือวิธีการที่ต้นแบบอนุญาตให้วัตถุสามารถสืบทอดฟังก์ชั่นจากวัตถุอื่น ๆ แล้วเห็นวิธีการรวมตัวดำเนินการใหม่และตัวสร้างเพื่อสร้างวัตถุ สิ่งที่กล่าวถึงที่นี่เป็นเพียงขั้นตอนแรกในการปลดล็อกพลังงานและความยืดหยุ่นของต้นแบบวัตถุ บทความนี้กระตุ้นให้คุณค้นพบและเรียนรู้ข้อมูลใหม่เกี่ยวกับต้นแบบและภาษาจาวาสคริปต์ด้วยตัวคุณเอง
นอกจากนี้โปรดขับอย่างระมัดระวัง คุณจะไม่มีทางรู้ว่าเทคโนโลยี (มีข้อบกพร่อง) ยานพาหนะเหล่านี้ที่เดินทางบนท้องถนนจะสืบทอดมาจากต้นแบบของพวกเขา
ลิงค์ต้นฉบับ: สคริปต์ Junkie Translation: Bole Online - Emje