ฉันพยายามทำความเข้าใจเบื้องหลังฉากหลังของจาวาสคริปต์และติดอยู่ในความเข้าใจในการสร้างวัตถุในตัววัตถุและฟังก์ชั่นพิเศษและความสัมพันธ์ระหว่างพวกเขา
มันซับซ้อนมันง่ายต่อการเข้าใจผิดและหนังสือ Javascript มือใหม่มากมายหลายเล่มเข้าใจผิดดังนั้นอย่าเชื่อใจทุกสิ่งที่คุณอ่าน
ฉันเป็นหนึ่งในผู้ดำเนินการของโปรแกรม JS ของ Microsoft ในปี 1990 และในคณะกรรมการมาตรฐานและฉันทำข้อผิดพลาดจำนวนมากเมื่อรวมคำตอบนี้เข้าด้วยกัน (ถึงแม้ว่าฉันจะไม่ได้ทำงานในเรื่องนี้มานานกว่า 15 ปีฉันก็อาจได้รับการให้อภัย) มันเป็นเรื่องยุ่งยาก แต่เมื่อคุณเข้าใจการถ่ายทอดทางพันธุกรรมต้นแบบแล้วมันก็สมเหตุสมผล
เมื่อฉันอ่านว่าวัตถุในตัวทั้งหมดเช่น Array, String ฯลฯ เป็นส่วนขยาย (สืบทอด) จาก Object ฉันคิดว่า Object เป็นวัตถุแรกที่สร้างขึ้นในวัตถุที่สร้างขึ้นและส่วนที่เหลือของวัตถุสืบทอดมาจากมัน
เริ่มต้นด้วยการทิ้งทุกสิ่งที่คุณรู้เกี่ยวกับการสืบทอดตามระดับ JS ใช้การสืบทอดตามต้นแบบ
ถัดไปตรวจสอบให้แน่ใจว่าคุณมีคำจำกัดความที่ชัดเจนมากในหัวของความหมายของ "การสืบทอด" ผู้ใช้ภาษา OO เช่น C # หรือ Java หรือ C ++ คิดว่าการสืบทอดหมายถึงการพิมพ์ย่อย แต่การสืบทอดไม่ได้หมายถึงการพิมพ์ย่อย การสืบทอดหมายความว่าสมาชิกของสิ่งหนึ่งยังเป็นสมาชิกของอีกสิ่งหนึ่งด้วย มันไม่จำเป็นต้องหมายความว่ามีความสัมพันธ์ระหว่าง subtyping สิ่งเหล่านั้น! ความเข้าใจผิดมากมายในทฤษฎีประเภทเป็นผลมาจากคนไม่ทราบว่ามีความแตกต่าง
แต่มันก็ไม่สมเหตุสมผลเมื่อคุณรู้ว่า Object สามารถสร้างได้โดยฟังก์ชั่นเท่านั้น แต่ฟังก์ชั่นนั้นก็ไม่ได้มี แต่วัตถุของ Function
นี่เป็นเพียงแค่ความเท็จ วัตถุบางอย่างจะไม่ได้สร้างขึ้นโดยการเรียกฟังก์ชั่นบางอย่างnew F
F
วัตถุบางอย่างถูกสร้างขึ้นโดยรันไทม์ของ JS ไม่มีอะไรเลย มีไข่ที่ไม่ได้ถูกวางโดยไก่ใด ๆ พวกเขาเพิ่งสร้างขึ้นโดยรันไทม์เมื่อมันเริ่มต้นขึ้น
สมมติว่ากฎคืออะไรและอาจช่วยได้
- ทุกอินสแตนซ์ของวัตถุมีวัตถุต้นแบบ
null
ในบางกรณีที่ต้นแบบสามารถ
- ถ้าคุณเข้าถึงสมาชิกบนอินสแตนซ์ของวัตถุและวัตถุนั้นไม่มีสมาชิกนั้นวัตถุจะเลื่อนไปที่ต้นแบบหรือหยุดหากต้นแบบนั้นเป็นโมฆะ
prototype
สมาชิกของวัตถุที่เป็นปกติไม่ได้ต้นแบบของวัตถุ
- ค่อนข้าง
prototype
สมาชิกของฟังก์ชั่น F new F()
วัตถุเป็นวัตถุที่จะกลายเป็นต้นแบบของวัตถุที่สร้างขึ้นโดย
- ในการใช้งานบางอย่างอินสแตนซ์รับ
__proto__
สมาชิกที่ให้ต้นแบบจริง ๆ (ตอนนี้เลิกใช้แล้วอย่าพึ่งมัน)
- วัตถุฟังก์ชั่นจะได้รับวัตถุเริ่มต้นใหม่ที่กำหนดให้
prototype
เมื่อพวกเขาถูกสร้างขึ้น
Function.prototype
ต้นแบบของวัตถุฟังก์ชั่นเป็นของหลักสูตร
มาสรุปกันเถอะ
- ต้นแบบของ
Object
คือFunction.prototype
Object.prototype
เป็นวัตถุต้นแบบวัตถุ
- ต้นแบบของ
Object.prototype
คือnull
- ต้นกำเนิดของ
Function
คือFunction.prototype
- นี่เป็นหนึ่งในสถานการณ์ที่หายากซึ่งFunction.prototype
ต้นแบบของFunction
!
Function.prototype
เป็นวัตถุต้นแบบฟังก์ชั่น
- ต้นแบบของ
Function.prototype
คือObject.prototype
สมมุติว่าเราสร้างฟังก์ชั่น Foo
- ต้นแบบของการมี
Foo
Function.prototype
Foo.prototype
เป็นวัตถุต้นแบบฟู
- ต้นแบบของการมี
Foo.prototype
Object.prototype
สมมติว่าเราพูด new Foo()
- ต้นแบบของวัตถุใหม่คือ
Foo.prototype
ตรวจสอบให้แน่ใจว่าเหมาะสม มาวาดกันเถอะ Ovals เป็นวัตถุวัตถุ ขอบมี__proto__
ความหมายว่า "ต้นแบบของ" หรือprototype
ความหมาย " prototype
คุณสมบัติของ"
รันไทม์ทั้งหมดที่ต้องทำคือสร้างวัตถุเหล่านั้นทั้งหมดและกำหนดคุณสมบัติต่าง ๆ ให้สอดคล้องกัน ฉันแน่ใจว่าคุณสามารถดูว่าจะทำอย่างไร
ลองมาดูตัวอย่างที่ทดสอบความรู้ของคุณ
function Car(){ }
var honda = new Car();
print(honda instanceof Car);
print(honda.constructor == Car);
สิ่งที่พิมพ์นี้
แล้วมันinstanceof
หมายความว่าอะไร? honda instanceof Car
หมายความว่า " Car.prototype
เท่ากับวัตถุใด ๆ ในhonda
โซ่ต้นแบบ"
ใช่แล้ว. honda
ต้นแบบคือCar.prototype
ดังนั้นเราก็เสร็จแล้ว สิ่งนี้พิมพ์จริง
แล้วอันที่สองล่ะ?
honda.constructor
Car.prototype
ไม่ได้อยู่ดังนั้นเราจึงปรึกษาต้นแบบซึ่งเป็น เมื่อCar.prototype
วัตถุถูกสร้างขึ้นมันจะได้รับคุณสมบัติโดยอัตโนมัติconstructor
เท่ากับCar
สิ่งนี้จึงเป็นจริง
แล้วเรื่องนี้ล่ะ
var Animal = new Object();
function Reptile(){ }
Reptile.prototype = Animal;
var lizard = new Reptile();
print(lizard instanceof Reptile);
print(lizard.constructor == Reptile);
โปรแกรมนี้พิมพ์อะไร
อีกครั้งlizard instanceof Reptile
หมายถึง " Reptile.prototype
เท่ากับวัตถุใด ๆ ในlizard
โซ่ต้นแบบ"
ใช่แล้ว. lizard
ต้นแบบคือReptile.prototype
ดังนั้นเราก็เสร็จแล้ว สิ่งนี้พิมพ์จริง
ตอนนี้เกี่ยวกับ
print(lizard.constructor == Reptile);
คุณอาจคิดว่าสิ่งนี้พิมพ์ออกมาได้จริงเนื่องจากlizard
สร้างด้วยnew Reptile
แต่คุณจะผิด เหตุผลมันออกมา
- ไม่
lizard
ได้มีconstructor
ทรัพย์สิน? ไม่เลยเราดูต้นแบบ
- ต้นแบบของการ
lizard
มีที่ซึ่งเป็นReptile.prototype
Animal
- ไม่
Animal
ได้มีconstructor
ทรัพย์สิน? ไม่เลยเราดูที่ต้นแบบ
- ต้นแบบของ
Animal
เป็นObject.prototype
และถูกสร้างขึ้นโดยรันไทม์และเท่ากับObject.prototype.constructor
Object
- ดังนั้นสิ่งนี้จึงพิมพ์ผิด
เราควรจะพูดReptile.prototype.constructor = Reptile;
ในจุดนั้น แต่เราจำไม่ได้!
ตรวจสอบให้แน่ใจว่าทั้งหมดเหมาะสมกับคุณ วาดกล่องและลูกศรบางส่วนหากยังคงสับสน
สิ่งที่สับสนอย่างยิ่งคือถ้าฉันconsole.log(Function.prototype)
พิมพ์ฟังก์ชั่น แต่เมื่อฉันพิมพ์console.log(Object.prototype)
มันจะพิมพ์วัตถุ ทำไมFunction.prototype
ฟังก์ชั่นเมื่อมันถูกหมายถึงเป็นวัตถุ?
undefined
ต้นแบบฟังก์ชั่นที่ถูกกำหนดให้เป็นหน้าที่ซึ่งเมื่อเรียกว่าผลตอบแทน เรารู้แล้วว่าFunction.prototype
มันFunction
เป็นต้นแบบที่แปลกพอสมควร ดังนั้นจึงFunction.prototype()
เป็นเรื่องถูกกฎหมายและเมื่อคุณทำคุณจะได้รับundefined
กลับ ดังนั้นมันจึงเป็นฟังก์ชั่น
Object
ต้นแบบไม่ได้มีคุณสมบัตินี้; มันไม่สามารถโทรออกได้ มันเป็นแค่วัตถุ
เมื่อคุณconsole.log(Function.prototype.constructor)
เป็นอีกฟังก์ชั่น
Function.prototype.constructor
เป็นเพียงFunction
ชัดเจน และFunction
เป็นฟังก์ชั่น
ตอนนี้คุณจะใช้บางสิ่งเพื่อสร้างมันเองได้อย่างไร (Mind = blown)
คุณคิดมากเกินไป สิ่งที่จำเป็นทั้งหมดก็คือรันไทม์จะสร้างกลุ่มวัตถุเมื่อมันเริ่มขึ้น วัตถุเป็นเพียงตารางการค้นหาที่เชื่อมโยงสตริงกับวัตถุ เมื่อรันไทม์เริ่มขึ้นทั้งหมดจะได้ทำคือการสร้างวัตถุว่างเปล่าไม่กี่โหลและจากนั้นเริ่มกำหนดprototype
, __proto__
, constructor
และอื่น ๆ คุณสมบัติของแต่ละวัตถุจนกว่าพวกเขาจะทำให้กราฟที่พวกเขาต้องการที่จะทำให้
มันจะมีประโยชน์ถ้าคุณทำแผนภาพนั้นที่ฉันให้ไว้ด้านบนแล้วเพิ่มconstructor
ขอบเข้าไป คุณจะเห็นได้อย่างรวดเร็วว่านี่เป็นกราฟวัตถุที่ง่ายมากและรันไทม์จะไม่มีปัญหาในการสร้าง
การออกกำลังกายที่ดีก็คือการทำด้วยตัวเอง ที่นี่ฉันจะเริ่มต้นให้คุณออก เราจะใช้my__proto__
เพื่อหมายถึง "วัตถุต้นแบบของ" และmyprototype
หมายถึง "คุณสมบัติต้นแบบของ"
var myobjectprototype = new Object();
var myfunctionprototype = new Object();
myfunctionprototype.my__proto__ = myobjectprototype;
var myobject = new Object();
myobject.myprototype = myobjectprototype;
และอื่น ๆ คุณสามารถเติมส่วนที่เหลือของโปรแกรมเพื่อสร้างชุดของวัตถุที่มีโทโพโลยีเดียวกันกับวัตถุในตัว Javascript ที่ "จริง" ได้หรือไม่? ถ้าคุณทำเช่นนั้นคุณจะพบว่ามันง่ายมาก
วัตถุใน JavaScript มีตารางการค้นหาเพียงว่าสายเชื่อมโยงกับวัตถุอื่น แค่นั้นแหละ! ไม่มีเวทย์มนตร์ที่นี่ คุณกำลังผูกตัวเป็นปมเพราะคุณกำลังจินตนาการถึงข้อ จำกัด ที่ไม่มีอยู่จริงเช่นเดียวกับที่วัตถุทุกชิ้นจะต้องสร้างขึ้นโดยผู้สร้าง
ฟังก์ชั่นเป็นเพียงวัตถุที่มีความสามารถเพิ่มเติม: ถูกเรียก ดังนั้นให้ลองผ่านโปรแกรมจำลองสถานการณ์เล็ก ๆ ของคุณและเพิ่ม.mycallable
คุณสมบัติให้กับทุกวัตถุที่ระบุว่าสามารถเรียกได้หรือไม่ มันง่ายอย่างนั้น
Function.prototype
สามารถเป็นฟังก์ชั่นและมีเขตข้อมูลภายใน ดังนั้นคุณจะไม่เรียกใช้ฟังก์ชันต้นแบบเมื่อผ่านโครงสร้าง ในที่สุดจำไว้ว่ามีการตีความเครื่องยนต์ Javascript ดังนั้นวัตถุและฟังก์ชั่นอาจถูกสร้างขึ้นภายในเครื่องยนต์และไม่ได้มาจาก Javascript และการอ้างอิงพิเศษเช่นFunction.prototype
และObject.prototype
อาจถูกตีความในลักษณะพิเศษโดยเครื่องยนต์