เพื่อทำความเข้าใจต้นแบบใน JS คุณต้องเข้าใจแนวคิดต่อไปนี้ก่อน
1. ทุกอย่างใน JS เป็นวัตถุ
2. ทุกอย่างใน JS นั้นมาจากวัตถุนั่นคือจุดสิ้นสุดของห่วงโซ่ต้นแบบของทุกสิ่งชี้ไปที่ Object.prototype
// ["Constructor", "ToString", "Tolocalestring", "Valueof", "HasownProperty", "Isprototypeof", // "Propertyisenumerable", "__definegetter__" console.log (Object.getOwnPropertyNames (Object.prototype));
3. ความสัมพันธ์ที่ลึกซึ้งระหว่างตัวสร้างและอินสแตนซ์ (วัตถุ) ใน JS
ตัวสร้างกำหนดต้นแบบเพื่อเห็นด้วยกับข้อกำหนดของอินสแตนซ์ของพวกเขาจากนั้นสร้างอินสแตนซ์ผ่านใหม่ ฟังก์ชั่นของพวกเขาคือการผลิตวัตถุ
ตัวสร้าง (วิธีการ) เองเป็นอินสแตนซ์ของวิธีการ (ฟังก์ชั่น) ดังนั้นจึงสามารถพบได้สำหรับ __proto __ (protochain)
Object / function f () {} นี่คือตัวสร้างหนึ่งรายการนั้นจัดทำโดย JS Native API และอื่น ๆ ได้รับการปรับแต่ง
วัตถุใหม่ () / ใหม่ f () เป็นอินสแตนซ์
ตัวอย่างสามารถดูได้ "เท่านั้น" เพื่อค้นหาว่าต้นแบบใดที่สร้างขึ้นตาม
ต้นแบบที่ "ไม่สามารถ" นิยามอินสแตนซ์ใหม่แล้ว deludes ตัวเองเพื่อสร้างอินสแตนซ์ของอินสแตนซ์
ฝึกฝนเพื่อสร้างความรู้ที่แท้จริงและโดยการสังเกต/คิดว่าตัวเองสามารถเข้าใจได้อย่างแท้จริง:
// ก่อนอื่นให้ดูว่าตัวสร้างคืออะไร // ฟังก์ชั่นว่างเปล่า () {} ฟังก์ชันว่างเปล่า () {} console.log (function.prototype, function .__ proto__); // object {} ฟังก์ชันว่าง () {} console.log (object.prototype, Object .__ Proto__); ฟังก์ชั่น f () {} // f {} ฟังก์ชั่นว่างเปล่า () {} console.log (f.prototype, f .__ proto__);คุณอาจเวียนหัวมาทำลายมันลง
ต้นแบบ
รูปแบบของเอาต์พุตต้นแบบคือ: ตัวสร้างชื่อต้นแบบ
ก่อนอื่นมาดูว่า Object.prototype outputs?
วัตถุ {} -> วัตถุก่อนหน้านี้เป็นชื่อของตัวสร้างและวัตถุถัดไปแสดงถึงต้นแบบ นี่คือ {} นั่นคืออินสแตนซ์ของวัตถุวัตถุ (วัตถุเปล่า)
จากนั้น f {} เราเข้าใจว่ามันหมายถึงอะไร F คือชื่อของตัวสร้างและต้นแบบก็เป็นวัตถุที่ว่างเปล่า
// ลองดูตัวอย่างที่สร้างโดยตัวสร้าง var o = วัตถุใหม่ (); // var o = {}; // วัตถุที่ไม่ได้กำหนด {} console.log (o.prototype, o .__ proto__); ฟังก์ชั่น f () {} var i = new f (); // undefined f {} console.log (i.prototype, i .__ proto__);มาลึกกว่ากันนิดหน่อยและกำหนดต้นแบบของ F เพื่อดูว่าจะเกิดอะไรขึ้น?
ฟังก์ชั่น f () {} f.prototype.a = function () {}; var i = new f (); // undefined f {a: function} console.log (i.prototype, i .__ proto__);ด้วยวิธีนี้เราสามารถเห็นได้อย่างชัดเจนว่าฉันถูกสร้างขึ้นจาก F ต้นแบบคือ {a: function} ซึ่งหมายความว่าวิธี A ได้ถูกเพิ่มเข้าไปในต้นแบบวัตถุเปล่าดั้งเดิม
ลองเปลี่ยนสถานการณ์จะเกิดอะไรขึ้นถ้าต้นแบบที่ครอบคลุม F อย่างสมบูรณ์?
ฟังก์ชั่น f () {} f.prototype = {a: function () {}}; var i = new f (); // วัตถุที่ไม่ได้กำหนด {a: function} console.log (i.prototype, i .__ proto__);เฮ้ ~ ทำไมมันถึงระบุที่นี่ว่าฉันถูกสร้างขึ้นจากวัตถุ? เลขที่!
เนื่องจากเราเขียนทับต้นแบบของ F อย่างสมบูรณ์ในความเป็นจริงเราจึงระบุต้นแบบเป็นวัตถุ {a: function} แต่สิ่งนี้จะทำให้ข้อมูลตัวสร้างดั้งเดิมหายไปและกลายเป็นตัวสร้างที่ระบุโดยวัตถุ {a: function}
ตัวสร้างของวัตถุ {a: function} คืออะไร?
เพราะวัตถุ {a: function} นั้นสัมพันธ์กับจริง
var o = {a: function () {}} // วัตถุใหม่จากนั้นตัวสร้างของ O เป็นวัตถุแน่นอน
มาแก้ไขข้อผิดพลาดนี้
ฟังก์ชั่น f () {} f.prototype = {a: function () {}} // re-specify ตัวสร้างที่ถูกต้อง f.prototype.constructor = f; var i = new f (); // undefined f {a: ฟังก์ชั่น, constructor: function} console.log (i.prototype, i .__ proto__);ตอนนี้คุณสามารถรับข้อมูลต้นแบบที่ถูกต้องได้อีกครั้ง ~
ห่วงโซ่ต้นแบบ
ถ้าอย่างนั้นมาดูกันว่าห่วงโซ่ต้นแบบคืออะไร
พูดง่ายๆคือมันเหมือนกับความสัมพันธ์การสืบทอด (โซ่) ใน OOP ค้นหาเลเยอร์ด้วยเลเยอร์จนกระทั่งวัตถุสุดท้าย prototype
สิ่งที่สำคัญที่สุดคือการหาสิ่งที่สิ่งต่าง ๆ ใน JS คือวัตถุ (อินสแตนซ์) นี่เป็นเรื่องง่ายทุกอย่างใน JS คือวัตถุ!
จากนั้นเราต้องคิดว่าวัตถุใด ๆ มีต้นแบบ!
แล้วมาพิสูจน์กันเถอะ:
วัตถุ // นี่คือฟังก์ชั่นฟังก์ชั่นเป็นวัตถุอินสแตนซ์ของฟังก์ชั่นดังนั้นจึงเป็นวัตถุ. __ proto__ == function.prototype // จากนั้นต้นแบบของวัตถุจริง // นี่คือวัตถุธรรมดาดังนั้นอินสแตนซ์ของวัตถุเป็นของฟังก์ชัน prototype. Object.prototype .__ proto__ == null // ฟังก์ชั่นจริง // นี่เป็นฟังก์ชันใช่! ฟังก์ชั่น .__ proto__ == function.prototype // true function a () {} // นี่คือฟังก์ชั่นที่กำหนดเองและมันก็ยังคงเป็นฟังก์ชั่นหลังจากทั้งหมดนั่นถูกต้อง! a .__ proto__ == function.prototype // ฟังก์ชั่นใด ๆ เป็นอินสแตนซ์ของฟังก์ชันดังนั้นต้นแบบของ A? var a = new a () a .__ proto__ == a.prototype // อินสแตนซ์ A ถูกสร้างขึ้นโดย A Constructor ดังนั้นต้นแบบของ A ถูกกำหนดโดยแอตทริบิวต์ต้นแบบของ A.Prototype .__ Proto__ == Object.prototype // ตัวอย่างของวัตถุธรรมดาต้นแบบและ __proto__
แต่ละวัตถุมี __proto__ ชี้ไปที่ "ต้นแบบ" ของวัตถุ
สิ่งที่คล้ายกันคือแต่ละฟังก์ชั่นมีต้นแบบ วัตถุต้นแบบนี้คืออะไร?
ลองดูที่รหัสต่อไปนี้โดยใช้ตัวสร้างเพื่อสร้างวัตถุ (ด้านบนคือการสร้างวัตถุในรูปแบบของรูปแบบที่แท้จริง)
function foo () {}; var foo = new foo (); console.log (foo .__ proto__);แค่คิดว่า __proto__ ของวัตถุ foo นี้จะชี้ไปที่อะไร?
วัตถุที่มีแอตทริบิวต์ตัวสร้าง? ไม่สำคัญว่าคุณจะไม่เข้าใจมากนัก พิมพ์แอตทริบิวต์ต้นแบบของฟังก์ชั่น foo และเปรียบเทียบกับคุณเพื่อให้คุณรู้
function foo () {}; var foo = new foo (); console.log (foo .__ proto __); console.log (foo.prototype); console.log (foo .__ proto__ === foo.prototype);ปรากฎว่า __proto__ ของวัตถุ foo ที่ออกมาจากจุดใหม่เพียงจุดเดียวกับต้นแบบของฟังก์ชัน foo
foo .__ proto__ -> foo.prototype
อะไรคือจุดสำคัญของการออกแบบ js เช่นนี้? การระลึกถึงสิ่งที่กล่าวถึงข้างต้นในโลกของ JS วัตถุไม่ได้ถูกสร้างขึ้นตามคลาส (แม่พิมพ์) แต่ได้มาจากต้นแบบ (วัตถุอื่น)
เมื่อเราดำเนินการใหม่เพื่อสร้างวัตถุใหม่เราจะไม่ลึกเข้าไปในการดำเนินการเฉพาะของการดำเนินการใหม่ แต่เรามั่นใจในสิ่งหนึ่ง - นั่นคือเราชี้ไปที่วัตถุต้นแบบสำหรับวัตถุใหม่ __proto__
แค่รหัสนี้
function foo () {}; var foo = new foo ();Foo .__ Proto__ ชี้ไปที่ใคร? ทำไมคุณไม่สามารถชี้ไปที่ฟังก์ชั่น foo เองได้? แม้ว่าฟังก์ชั่นจะเป็นวัตถุ แต่ฉันจะพูดถึงรายละเอียดหากคุณมีโอกาส แต่แน่นอนว่ามันไม่เหมาะสมที่จะชี้ไปที่ foo .__ proto__foo เพราะ foo เป็นฟังก์ชั่นที่มีรหัสตรรกะจำนวนมาก ในฐานะที่เป็นวัตถุมันไม่มีความหมายในการประมวลผลเชิงตรรกะที่สืบทอด สิ่งที่ต้องการที่จะสืบทอดคือคุณลักษณะของ "วัตถุต้นแบบ"
ดังนั้นแต่ละฟังก์ชั่นจะสร้างวัตถุต้นแบบโดยอัตโนมัติและ __proto__ ของวัตถุใหม่จากฟังก์ชั่นนี้ชี้ไปที่ต้นแบบของฟังก์ชันนี้
foo .__ proto__ -> foo.prototype
สรุป
หลังจากพูดมากฉันยังไม่ได้อธิบายอย่างสมบูรณ์ดังนั้นจึงเป็นการดีกว่าที่จะอยู่ในภาพก่อนหน้า ฉันได้อ้างอิงรูปภาพจากชาวเน็ตอื่น ๆ แต่ฉันมักจะรู้สึกว่าฉันไม่ได้อธิบายอย่างชัดเจนดังนั้นฉันจึงวาดรูปด้วยตัวเอง ถ้าฉันคิดว่าของฉันดีโปรดชอบ! (ฉันพยายามอย่างเต็มที่ที่จะวาดมัน)
มาถ่ายภาพนี้กันและจดจำข้อเท็จจริงต่อไปนี้:
1. มีแอตทริบิวต์ _proto_ ในแต่ละวัตถุ
ไม่มีแนวคิดของชั้นเรียน (แม่พิมพ์) ใน JS World วัตถุได้มาจากวัตถุอื่น (โปรโต) ดังนั้นจะมีแอตทริบิวต์ _proto_ ในแต่ละวัตถุที่ชี้ไปที่วัตถุต้นแบบ (ดูที่วัตถุ OBJ ที่กำหนดไว้ในรูปแบบตัวอักษรที่มุมซ้ายบนมันเปิดพื้นที่สำหรับเก็บคุณสมบัติของวัตถุในหน่วยความจำและสร้าง _proto_ ชี้ไปที่ต้นแบบ - วัตถุต้นแบบระดับบนสุด)
2. แต่ละฟังก์ชั่นมีคุณสมบัติต้นแบบ
เหตุใด "ตัวสร้าง" จึงเรียกว่าตัวสร้าง? เพราะมันต้องการสร้างวัตถุ ดังนั้นตามข้อเท็จจริงแรกข้างต้นคุณลักษณะ _proto_ ของใครที่สร้างวัตถุใหม่ที่สร้างขึ้นมา? คุณไม่สามารถชี้ไปที่ตัวสร้างเองได้ แม้ว่ามันจะเป็นวัตถุ แต่คุณไม่ต้องการให้วัตถุใหม่สืบทอดคุณสมบัติและวิธีการของฟังก์ชัน ดังนั้นตัวสร้างแต่ละตัวจะมีแอตทริบิวต์ต้นแบบชี้ไปที่วัตถุเป็นต้นแบบของวัตถุใหม่ที่สร้างโดยตัวสร้างนี้
3. ฟังก์ชั่นเป็นวัตถุ
แต่ละฟังก์ชั่นมีคุณสมบัติและวิธีการทั่วไปบางอย่างเช่นใช้ ()/call () ฯลฯ แต่วิธีการทั่วไปเหล่านี้ได้รับการสืบทอดอย่างไร ฟังก์ชั่นถูกสร้างขึ้นอย่างไร? แค่คิดว่าทุกอย่างเป็นวัตถุรวมถึงฟังก์ชั่นและเป็นวัตถุที่สร้างขึ้นผ่านตัวสร้าง จากนั้นตามข้อเท็จจริงที่สองด้านบนแต่ละฟังก์ชั่นจะมีต้นแบบที่ชี้ไปที่ตัวสร้าง ฟังก์ชั่นของตัวสร้างนี้คือฟังก์ชั่นและฟังก์ชั่นทั้งหมดใน JS ถูกสร้างขึ้นจากฟังก์ชั่น คุณสมบัติและวิธีการทั่วไปของฟังก์ชั่นจะถูกเก็บไว้ในฟังก์ชันวัตถุต้นแบบ prototype