ในการเขียนโปรแกรม เรามักจะต้องการทำอะไรสักอย่างและขยายมันออกไป
ตัวอย่างเช่น เรามีออบเจ็กต์ user
ที่มีคุณสมบัติและวิธีการ และต้องการทำให้ admin
และ guest
เป็นรูปแบบที่ปรับเปลี่ยนเล็กน้อย เราต้องการนำสิ่งที่เรามีใน user
กลับมาใช้ใหม่ ไม่ใช่คัดลอก/ปรับใช้วิธีการของมัน เพียงสร้างวัตถุใหม่ไว้ด้านบน
การสืบทอดต้นแบบ เป็นคุณลักษณะทางภาษาที่ช่วยในเรื่องนั้น
ใน JavaScript ออบเจ็กต์มีคุณสมบัติพิเศษที่ซ่อนอยู่ [[Prototype]]
(ตามชื่อในข้อกำหนด) ซึ่งเป็น null
หรืออ้างอิงถึงออบเจ็กต์อื่น วัตถุนั้นเรียกว่า "ต้นแบบ":
เมื่อเราอ่านคุณสมบัติจาก object
และคุณสมบัตินั้นหายไป JavaScript จะนำคุณสมบัตินั้นมาจากต้นแบบโดยอัตโนมัติ ในการเขียนโปรแกรมสิ่งนี้เรียกว่า "การสืบทอดต้นแบบ" และเร็วๆ นี้ เราจะศึกษาตัวอย่างมากมายของการสืบทอดดังกล่าว รวมถึงคุณลักษณะทางภาษาที่เจ๋งกว่าที่สร้างขึ้นบนมรดกนั้น
คุณสมบัติ [[Prototype]]
อยู่ภายในและซ่อนไว้ แต่มีหลายวิธีในการตั้งค่า
หนึ่งในนั้นคือการใช้ชื่อพิเศษ __proto__
เช่นนี้
ให้สัตว์ = { กิน: จริง - ให้กระต่าย = { กระโดด: จริง - rabbit.__proto__ = สัตว์; // ชุดกระต่าย[[ต้นแบบ]] = สัตว์
ตอนนี้ถ้าเราอ่านคุณสมบัติจาก rabbit
แล้วหายไป JavaScript จะนำคุณสมบัตินั้นมาจาก animal
โดยอัตโนมัติ
ตัวอย่างเช่น:
ให้สัตว์ = { กิน: จริง - ให้กระต่าย = { กระโดด: จริง - rabbit.__proto__ = สัตว์; - // เราสามารถหาคุณสมบัติทั้งสองได้ใน rabbit ตอนนี้: การแจ้งเตือน (rabbit.eats); // จริง (**) การแจ้งเตือน (rabbit.jumps); // จริง
ในที่นี้เส้น (*)
กำหนด animal
ให้เป็นต้นแบบของ rabbit
จากนั้น เมื่อ alert
พยายามอ่านคุณสมบัติ rabbit.eats
(**)
มันไม่ได้อยู่ใน rabbit
ดังนั้น JavaScript จึงติดตามการอ้างอิง [[Prototype]]
และค้นหาใน animal
(ดูจากล่างขึ้นบน):
ในที่นี้เราสามารถพูดได้ว่า " animal
เป็นต้นแบบของ rabbit
" หรือ " rabbit
ต้นแบบสืบทอดมาจาก animal
"
ดังนั้นหาก animal
มีคุณสมบัติและวิธีการที่เป็นประโยชน์มากมาย สัตว์เหล่านั้นจะมีอยู่ใน rabbit
โดยอัตโนมัติ ทรัพย์สินดังกล่าวเรียกว่า “มรดก”
หากเรามีวิธีการใน animal
ก็สามารถเรียก rabbit
ได้ :
ให้สัตว์ = { กิน: จริง, เดิน() { alert("สัตว์เดิน"); - - ให้กระต่าย = { กระโดด: จริง, __โปรโต__: สัตว์ - // เดินมาจากต้นแบบ กระต่าย.เดิน(); // สัตว์เดิน
วิธีการนี้จะถูกดึงมาจากต้นแบบโดยอัตโนมัติ เช่นนี้:
ห่วงโซ่ต้นแบบอาจยาวกว่านี้ได้:
ให้สัตว์ = { กิน: จริง, เดิน() { alert("สัตว์เดิน"); - - ให้กระต่าย = { กระโดด: จริง, __โปรโต__: สัตว์ - ให้ลองเอียร์ = { ความยาวหู: 10, __โปรโต__: กระต่าย - // เดินมาจากห่วงโซ่ต้นแบบ longEar.เดิน(); // สัตว์เดิน การแจ้งเตือน (longEar.jumps); // จริง (จากกระต่าย)
ตอนนี้ถ้าเราอ่านอะไรบางอย่างจาก longEar
และมันหายไป JavaScript จะค้นหามันด้วย rabbit
จากนั้นจึงค้นหาใน animal
มีเพียงสองข้อจำกัด:
การอ้างอิงไม่สามารถอยู่ในแวดวงได้ JavaScript จะแสดงข้อผิดพลาดหากเราพยายามกำหนด __proto__
ให้เป็นวงกลม
ค่าของ __proto__
สามารถเป็นได้ทั้งวัตถุหรือ null
ประเภทอื่นๆ จะถูกละเว้น
มันอาจจะชัดเจน แต่ก็ยังมี [[Prototype]]
ได้เพียงอันเดียวเท่านั้น วัตถุไม่อาจสืบทอดจากอีกสองวัตถุ
__proto__
เป็นผู้ทะเยอทะยาน/ผู้กำหนดประวัติศาสตร์สำหรับ [[Prototype]]
เป็นข้อผิดพลาดทั่วไปของนักพัฒนามือใหม่ที่ไม่ทราบความแตกต่างระหว่างทั้งสองนี้
โปรดทราบว่า __proto__
ไม่เหมือนกับ คุณสมบัติภายใน [[Prototype]]
มันเป็น getter/setter สำหรับ [[Prototype]]
ต่อมาเราจะเห็นสถานการณ์ที่มันสำคัญ สำหรับตอนนี้ เรามาจำไว้ในขณะที่เราสร้างความเข้าใจในภาษา JavaScript
คุณสมบัติ __proto__
ค่อนข้างล้าสมัย มันมีอยู่ด้วยเหตุผลทางประวัติศาสตร์ JavaScript สมัยใหม่แนะนำว่าเราควรใช้ฟังก์ชัน Object.getPrototypeOf/Object.setPrototypeOf
แทนการรับ/ตั้งค่าต้นแบบ เราจะกล่าวถึงฟังก์ชันเหล่านี้ในภายหลัง
ตามข้อกำหนด __proto__
ต้องได้รับการสนับสนุนโดยเบราว์เซอร์เท่านั้น แม้ว่าในความเป็นจริงแล้ว ทุกสภาพแวดล้อมรวมถึงการสนับสนุนฝั่งเซิร์ฟเวอร์ __proto__
ดังนั้นเราจึงค่อนข้างปลอดภัยในการใช้งาน
เนื่องจากสัญกรณ์ __proto__
นั้นชัดเจนกว่าเล็กน้อย เราจึงใช้มันในตัวอย่าง
ต้นแบบใช้สำหรับการอ่านคุณสมบัติเท่านั้น
การดำเนินการเขียน/ลบทำงานได้โดยตรงกับออบเจ็กต์
ในตัวอย่างด้านล่าง เรากำหนดวิธี walk
ของตัวเองให้กับ rabbit
:
ให้สัตว์ = { กิน: จริง, เดิน() { /* rabbit จะไม่ใช้วิธีนี้ */ - - ให้กระต่าย = { __โปรโต__: สัตว์ - rabbit.walk = ฟังก์ชั่น () { alert("กระต่าย! เด้ง-เด้ง!"); - กระต่าย.เดิน(); // กระต่าย! เด้งเด้ง!
จากนี้ไป การเรียก rabbit.walk()
จะค้นหาเมธอดในออบเจ็กต์ทันทีและดำเนินการโดยไม่ต้องใช้ต้นแบบ:
คุณสมบัติตัวเข้าถึงเป็นข้อยกเว้น เนื่องจากการมอบหมายจะถูกจัดการโดยฟังก์ชันตัวตั้งค่า ดังนั้นการเขียนคุณสมบัติดังกล่าวจึงเหมือนกับการเรียกใช้ฟังก์ชัน
ด้วยเหตุนี้ admin.fullName
จึงทำงานอย่างถูกต้องในโค้ดด้านล่าง:
ให้ผู้ใช้ = { ชื่อ: "จอห์น" นามสกุล: "สมิธ", ตั้งชื่อเต็ม (ค่า) { [this.name, this.surname] = value.split(" "); - รับชื่อเต็ม () { ส่งคืน `${this.name} ${this.surname}`; - - ให้ผู้ดูแลระบบ = { __โปรโต__: ผู้ใช้ คือผู้ดูแลระบบ: จริง - การแจ้งเตือน (admin.fullName); // จอห์น สมิธ (*) // ตัวเซ็ตทริกเกอร์! admin.fullName = "อลิซ คูเปอร์"; - การแจ้งเตือน (admin.fullName); // อลิซ คูเปอร์ สถานะของแอดมินแก้ไขแล้ว การแจ้งเตือน (user.fullName); // John Smith สถานะการป้องกันผู้ใช้
ที่นี่ในบรรทัด (*)
คุณสมบัติ admin.fullName
มีทะเยอทะยานในต้นแบบ user
ดังนั้นจึงถูกเรียกว่า และในบรรทัด (**)
คุณสมบัติมีตัวตั้งค่าอยู่ในต้นแบบจึงเรียกว่า
คำถามที่น่าสนใจอาจเกิดขึ้นในตัวอย่างด้านบน: ค่าของชุดภายใน this
set fullName(value)
คืออะไร? คุณสมบัติ this.name
และ this.surname
อยู่ที่ไหนเขียน: ลงใน user
หรือ admin
?
คำตอบนั้นง่ายมาก: this
ไม่ได้รับผลกระทบจากต้นแบบเลย
ไม่ว่าจะพบวิธีการที่ไหน: ในวัตถุหรือต้นแบบของมัน ในการเรียกเมธอด this
จะเป็นวัตถุที่อยู่หน้าจุดเสมอ
ดังนั้นผู้ตั้งค่าโทร admin.fullName=
ใช้ admin
เป็น this
ไม่ใช่ user
นั่นเป็นสิ่งที่สำคัญอย่างยิ่งจริงๆ เพราะเราอาจมีวัตถุขนาดใหญ่ที่มีหลายวิธี และมีวัตถุที่สืบทอดมาจากมัน และเมื่อออบเจ็กต์ที่สืบทอดเรียกใช้เมธอดที่สืบทอดมา พวกเขาจะแก้ไขเฉพาะสถานะของตนเองเท่านั้น ไม่ใช่สถานะของออบเจ็กต์ขนาดใหญ่
ตัวอย่างเช่น animal
ในที่นี้เป็นตัวแทนของ "วิธีการจัดเก็บ" และ rabbit
ก็ใช้ประโยชน์จากมัน
การเรียก rabbit.sleep()
ตั้งค่า this.isSleeping
บนวัตถุ rabbit
:
// สัตว์มีวิธีการ ให้สัตว์ = { เดิน() { ถ้า (!this.isSleeping) { alert(`ฉันเดิน`); - - นอน() { this.isSleeping = จริง; - - ให้กระต่าย = { ชื่อ: "กระต่ายขาว", __โปรโต__: สัตว์ - // แก้ไข rabbit.isSleeping กระต่าย.สลีป(); การแจ้งเตือน (rabbit.isSleeping); // จริง การแจ้งเตือน (animal.isSleeping); // ไม่ได้กำหนด (ไม่มีคุณสมบัติดังกล่าวในต้นแบบ)
ภาพที่ได้มา:
หากเรามีวัตถุอื่น เช่น bird
snake
ฯลฯ ที่สืบทอดมาจาก animal
พวกมันก็จะสามารถเข้าถึงวิธีการของ animal
ได้เช่นกัน แต่ this
ในการเรียกแต่ละวิธีจะเป็นวัตถุที่เกี่ยวข้อง ซึ่งประเมิน ณ เวลาการโทร (ก่อนจุด) ไม่ใช่ animal
ดังนั้นเมื่อเราเขียนข้อมูลลงใน this
มันจะถูกจัดเก็บไว้ในวัตถุเหล่านี้
เป็นผลให้มีการใช้วิธีการร่วมกัน แต่สถานะของวัตถุไม่ได้เป็นเช่นนั้น
for..in
วนซ้ำคุณสมบัติที่สืบทอดมาเช่นกัน
ตัวอย่างเช่น:
ให้สัตว์ = { กิน: จริง - ให้กระต่าย = { กระโดด: จริง, __โปรโต__: สัตว์ - // Object.keys ส่งคืนเฉพาะคีย์ของตัวเองเท่านั้น การแจ้งเตือน (Object.keys (กระต่าย)); //กระโดด // for..in วนซ้ำทั้งคีย์ของตัวเองและคีย์ที่สืบทอดมา สำหรับ (ให้เสาในกระต่าย) การแจ้งเตือน (เสา); //กระโดดแล้วกิน
หากนั่นไม่ใช่สิ่งที่เราต้องการ และเราต้องการแยกคุณสมบัติที่สืบทอดมาออก ก็จะมีเมธอดในตัว obj.hasOwnProperty(key) ซึ่งจะคืนค่า true
หาก obj
มีคุณสมบัติของตัวเอง (ไม่ได้รับการสืบทอด) ชื่อ key
เพื่อให้เราสามารถกรองคุณสมบัติที่สืบทอดมาได้ (หรือทำอย่างอื่นกับคุณสมบัติเหล่านั้น):
ให้สัตว์ = { กิน: จริง - ให้กระต่าย = { กระโดด: จริง, __โปรโต__: สัตว์ - สำหรับ (ให้ประคับประคองในกระต่าย) { ให้ isOwn = rabbit.hasOwnProperty(prop); ถ้า (เป็นเจ้าของ) { alert(`ของเรา: ${prop}`); // เรา: กระโดด } อื่น { alert(`รับมา: ${prop}`); // สืบทอดมา: กิน - -
ที่นี่เรามีสายโซ่การสืบทอดดังต่อไปนี้: rabbit
สืบทอดมาจาก animal
ที่สืบทอดมาจาก Object.prototype
(เนื่องจาก animal
เป็นวัตถุตามตัวอักษร {...}
ดังนั้นจึงเป็นค่าเริ่มต้น) จากนั้นจึงเป็น null
ที่อยู่ด้านบน:
โปรดทราบว่ามีเรื่องตลกอย่างหนึ่ง เมธอด rabbit.hasOwnProperty
มาจากไหน เราไม่ได้กำหนดมัน เมื่อดูที่ chain เราจะเห็นว่า Object.prototype.hasOwnProperty
จัดเตรียมเมธอดนี้ไว้ กล่าวอีกนัยหนึ่ง มันสืบทอดมา
…แต่ทำไม hasOwnProperty
ถึงไม่ปรากฏในลูป for..in
เหมือน eats
และ jumps
เหมือนกัน ถ้า for..in
แสดงรายการคุณสมบัติที่สืบทอดมา
คำตอบนั้นง่าย: นับไม่ได้ เช่นเดียวกับคุณสมบัติอื่น ๆ ทั้งหมดของ Object.prototype
มันมีธง enumerable:false
และ for..in
แสดงรายการคุณสมบัติที่นับได้เท่านั้น นั่นเป็นสาเหตุว่าทำไมคุณสมบัติ Object.prototype
และส่วนที่เหลือจึงไม่อยู่ในรายการ
วิธีการรับคีย์/ค่าอื่นๆ เกือบทั้งหมดจะละเว้นคุณสมบัติที่สืบทอดมา
วิธีการรับคีย์/ค่าอื่นๆ เกือบทั้งหมด เช่น Object.keys
, Object.values
และอื่นๆ จะละเว้นคุณสมบัติที่สืบทอดมา
พวกมันทำงานบนวัตถุเท่านั้น คุณสมบัติจากต้นแบบจะ ไม่ ถูกนำมาพิจารณา
ใน JavaScript วัตถุทั้งหมดมีคุณสมบัติ [[Prototype]]
ที่ซ่อนอยู่ซึ่งเป็นวัตถุอื่นหรือ null
เราสามารถใช้ obj.__proto__
เพื่อเข้าถึงมันได้ (ผู้ทะเยอทะยาน/ผู้กำหนดประวัติศาสตร์ มีวิธีอื่นที่จะกล่าวถึงเร็วๆ นี้)
วัตถุที่อ้างอิงโดย [[Prototype]]
เรียกว่า "ต้นแบบ"
หากเราต้องการอ่านคุณสมบัติของ obj
หรือเรียกใช้เมธอด แต่ไม่มีอยู่ JavaScript จะพยายามค้นหามันในต้นแบบ
การดำเนินการเขียน/ลบดำเนินการกับออบเจ็กต์โดยตรง โดยไม่ได้ใช้ต้นแบบ (สมมติว่าเป็นคุณสมบัติของข้อมูล ไม่ใช่ตัวตั้งค่า)
หากเราเรียก obj.method()
และ method
ถูกนำมาจากต้นแบบ this
ยังคงอ้างอิงถึง obj
ดังนั้นเมธอดจะทำงานกับอ็อบเจ็กต์ปัจจุบันได้เสมอแม้ว่าจะสืบทอดมาก็ตาม
for..in
วนซ้ำทั้งคุณสมบัติของตัวเองและคุณสมบัติที่สืบทอดมา วิธีการรับคีย์/ค่าอื่นๆ ทั้งหมดจะทำงานบนออบเจ็กต์เท่านั้น
ความสำคัญ: 5
นี่คือโค้ดที่สร้างคู่ของอ็อบเจ็กต์ จากนั้นทำการแก้ไข
ค่าใดบ้างที่แสดงอยู่ในกระบวนการ?
ให้สัตว์ = { กระโดด: null - ให้กระต่าย = { __โปรโต__: สัตว์ กระโดด: จริง - การแจ้งเตือน (rabbit.jumps); - (1) ลบ rabbit.jumps; การแจ้งเตือน (rabbit.jumps); - (2) ลบ Animal.jumps; การแจ้งเตือน (rabbit.jumps); - (3)
ควรมี 3 คำตอบ
true
นำมาจาก rabbit
null
นำมาจาก animal
undefined
ไม่มีคุณสมบัติดังกล่าวอีกต่อไป
ความสำคัญ: 5
งานมีสองส่วน
โดยได้รับวัตถุมงคลดังนี้
ให้หัว = { แว่นตา: 1 - ให้ตาราง = { ปากกา: 3 - ให้นอน = { แผ่นงาน: 1, หมอน: 2 - ให้กระเป๋า = { เงิน: 2000 -
ใช้ __proto__
เพื่อกำหนดต้นแบบในลักษณะที่การค้นหาคุณสมบัติจะเป็นไปตามเส้นทาง: pockets
→ bed
→ table
→ head
ตัวอย่างเช่น pockets.pen
ควรเป็น 3
(พบใน table
) และ bed.glasses
ควรเป็น 1
(พบใน head
)
ตอบคำถาม: ได้ glasses
แบบ pockets.glasses
หรือ head.glasses
เร็วกว่ากัน ? เกณฑ์มาตรฐานหากจำเป็น
มาเพิ่ม __proto__
:
ให้หัว = { แว่นตา: 1 - ให้ตาราง = { ปากกา: 3, __โปรโต__: หัว - ให้นอน = { แผ่นงาน: 1, หมอน: 2, __โปรโต__: ตาราง - ให้กระเป๋า = { เงิน: 2000, __โปรโต__: เตียง - การแจ้งเตือน (pocket.pen); // 3 alert( เตียงแว่นตา ); // 1 การแจ้งเตือน( table.money ); // ไม่ได้กำหนด
ในเครื่องยนต์สมัยใหม่ เมื่อพิจารณาถึงประสิทธิภาพแล้ว ก็ไม่มีความแตกต่างไม่ว่าเราจะรับคุณสมบัติจากวัตถุหรือต้นแบบของมันก็ตาม พวกเขาจำได้ว่าทรัพย์สินนั้นพบที่ไหนและนำมาใช้ซ้ำในคำขอครั้งต่อไป
ตัวอย่างเช่น สำหรับ pockets.glasses
พวกเขาจำได้ว่าพบ glasses
ที่ไหน (ใน head
) และครั้งต่อไปจะค้นหาตรงนั้น พวกเขายังฉลาดพอที่จะอัปเดตแคชภายในหากมีการเปลี่ยนแปลงบางอย่าง เพื่อให้การปรับให้เหมาะสมมีความปลอดภัย
ความสำคัญ: 5
เรามี rabbit
ที่สืบทอดมาจาก animal
ถ้าเราเรียก rabbit.eat()
ว่า object ใดได้รับคุณสมบัติ full
: animal
หรือ rabbit
?
ให้สัตว์ = { กิน() { นี่เต็ม = จริง; - - ให้กระต่าย = { __โปรโต__: สัตว์ - กระต่าย.กิน();
คำตอบ: rabbit
.
นั่นเป็นเพราะว่า this
คือวัตถุก่อนจุด ดังนั้น rabbit.eat()
จึงแก้ไข rabbit
การค้นหาทรัพย์สินและการดำเนินการเป็นสองสิ่งที่แตกต่างกัน
พบเมธอด rabbit.eat
ครั้งแรกในต้นแบบ จากนั้นจึงดำเนินการด้วย this=rabbit
ความสำคัญ: 5
เรามีแฮมสเตอร์สองตัว: speedy
และ lazy
ซึ่งสืบทอดมาจากวัตถุ hamster
ทั่วไป
พอเราป้อนอันหนึ่ง อีกอันก็อิ่มด้วย ทำไม เราจะแก้ไขได้อย่างไร?
ให้หนูแฮมสเตอร์ = { ท้อง: [], กิน (อาหาร) { this.stomach.push(อาหาร); - - ให้เร็ว = { __โปรโต__: หนูแฮมสเตอร์ - ปล่อยให้ขี้เกียจ = { __โปรโต__: หนูแฮมสเตอร์ - //อันนี้เจอของกิน speedy.eat("แอปเปิ้ล"); การแจ้งเตือน ( ท้องเร็ว ); // แอปเปิล // อันนี้ก็มีด้วย เพราะอะไร? โปรดแก้ไข การแจ้งเตือน (ขี้เกียจท้อง); // แอปเปิล
มาดูสิ่งที่เกิดขึ้นในการโทร speedy.eat("apple")
กันดีกว่า
พบเมธอด speedy.eat
ในต้นแบบ ( =hamster
) จากนั้นดำเนินการด้วย this=speedy
(วัตถุก่อนจุด)
จากนั้น this.stomach.push()
จำเป็นต้องค้นหาคุณสมบัติ stomach
และเรียกใช้ push
มันมองหา stomach
ใน this
( =speedy
) แต่ไม่พบอะไรเลย
จากนั้นก็เดินตามห่วงโซ่ต้นแบบและพบ stomach
ใน hamster
จากนั้นมันจะเรียกมันว่า push
โดยใส่อาหารเข้าไปใน ท้องของต้นแบบ
ดังนั้นหนูแฮมสเตอร์ทุกตัวจึงมีท้องเดียว!
ทั้งสำหรับ lazy.stomach.push(...)
และ speedy.stomach.push()
คุณสมบัติ stomach
จะพบได้ในต้นแบบ (เนื่องจากไม่ได้อยู่ในวัตถุ) จากนั้นข้อมูลใหม่จะถูกผลักเข้าไป
โปรดทราบว่าสิ่งนั้นจะไม่เกิดขึ้นในกรณีของการมอบหมายงานง่ายๆ this.stomach=
:
ให้หนูแฮมสเตอร์ = { ท้อง: [], กิน (อาหาร) { // กำหนดให้ this.stomach แทน this.stomach.push this.stomach = [อาหาร]; - - ให้เร็ว = { __โปรโต__: หนูแฮมสเตอร์ - ปล่อยให้ขี้เกียจ = { __โปรโต__: หนูแฮมสเตอร์ - //คนรวดเร็วเจออาหารแล้ว speedy.eat("แอปเปิ้ล"); การแจ้งเตือน ( ท้องเร็ว ); // แอปเปิล //คนขี้เกียจท้องว่าง การแจ้งเตือน (ขี้เกียจท้อง); // <ไม่มีอะไร>
ตอนนี้ทุกอย่างทำงานได้ดีแล้ว เพราะ this.stomach=
ไม่ได้ทำการค้นหาของ stomach
ค่าจะถูกเขียนลงในวัตถุ this
โดยตรง
นอกจากนี้ เราสามารถหลีกเลี่ยงปัญหานี้ได้โดยสิ้นเชิงโดยทำให้แน่ใจว่าแฮมสเตอร์แต่ละตัวมีท้องเป็นของตัวเอง:
ให้หนูแฮมสเตอร์ = { ท้อง: [], กิน (อาหาร) { this.stomach.push(อาหาร); - - ให้เร็ว = { __โปรโต__: หนูแฮมสเตอร์ ท้อง: [] - ปล่อยให้ขี้เกียจ = { __โปรโต__: หนูแฮมสเตอร์ ท้อง: [] - //คนรวดเร็วเจออาหารแล้ว speedy.eat("แอปเปิ้ล"); การแจ้งเตือน ( ท้องเร็ว ); // แอปเปิล //คนขี้เกียจท้องว่าง การแจ้งเตือน (ขี้เกียจท้อง); // <ไม่มีอะไร>
วิธีแก้ปัญหาทั่วไป ควรเขียนคุณสมบัติทั้งหมดที่อธิบายสถานะของวัตถุเฉพาะ เช่น stomach
ด้านบน ลงในวัตถุนั้น ที่ป้องกันปัญหาดังกล่าว