ภาพรวม
วัตถุทั้งหมดใน JavaScript มีห่วงโซ่มรดกของตัวเอง นั่นคือแต่ละวัตถุสืบทอดวัตถุอื่นซึ่งเรียกว่าวัตถุ "ต้นแบบ" ยกเว้น NULL มันไม่มีวัตถุต้นแบบของตัวเอง
ความสำคัญของวัตถุต้นแบบคือถ้าวัตถุ A เป็นต้นแบบของวัตถุ B, วัตถุ B สามารถรับคุณสมบัติและวิธีการทั้งหมดของวัตถุ A. object.getPrototypof วิธีการใช้เพื่อรับวัตถุต้นแบบของวัตถุปัจจุบัน
var p = object.getPrototypeof (OBJ);
ในรหัสข้างต้นวัตถุ P คือวัตถุต้นแบบของวัตถุ OBJ
วิธีการสร้างวัตถุใช้เพื่อสร้างวัตถุใหม่และสืบทอดวัตถุที่ระบุ
var obj = object.create (p);
ในรหัสข้างต้นต้นแบบของวัตถุ OBJ ที่สร้างขึ้นใหม่คือวัตถุ p
แอตทริบิวต์ __proto__ ที่ไม่ได้มาตรฐาน (สองขีดล่างด้านหน้าและด้านหลัง) สามารถเขียนวัตถุต้นแบบของวัตถุบางอย่างได้ อย่างไรก็ตามคุณควรลองใช้คุณสมบัตินี้ให้น้อยที่สุด แต่ใช้ Object.getPrototypeof () และ Object.setPrototypeof () เพื่ออ่านและเขียนวัตถุต้นแบบ
var obj = {}; var p = {}; obj .__ proto__ = p; object.getPrototypeof (obj) === p // trueรหัสด้านบนตั้งค่าวัตถุ P เป็นต้นแบบของวัตถุ OBJ ผ่านแอตทริบิวต์ __proto__
นี่คือตัวอย่างที่ใช้งานได้จริง
var a = {x: 1}; var b = {__proto__: a}; bx // 1ในรหัสด้านบนวัตถุ B ตั้งค่าวัตถุต้นแบบเป็นวัตถุผ่านแอตทริบิวต์ __proto__ ดังนั้นวัตถุ B สามารถรับคุณสมบัติและวิธีการทั้งหมดของวัตถุ A วัตถุ B นั้นไม่มีแอตทริบิวต์ X แต่เครื่องยนต์ JavaScript พบวัตถุต้นแบบ A ผ่านแอตทริบิวต์ __Proto__ จากนั้นอ่านแอตทริบิวต์ x ของ A
คำสั่งใหม่สร้างวัตถุอินสแตนซ์ใหม่ผ่านตัวสร้าง มันเป็นหลักผูกพันต้นแบบของวัตถุอินสแตนซ์กับคุณสมบัติต้นแบบของตัวสร้างและจากนั้นดำเนินการคอนสตรัคเตอร์บนวัตถุอินสแตนซ์
var o = new foo (); // เทียบเท่ากับ var o = วัตถุใหม่ (); o .__ proto__ = foo.prototype; foo.call (o);
แอตทริบิวต์ __proto__ ของวัตถุต้นแบบยังสามารถชี้ไปที่วัตถุอื่น ๆ ซึ่งจะสร้างระดับ "chain prototype chain" ตามระดับ
var a = {x: 1}; var b = {__proto__: a}; var c = {__proto__: b}; cx // 1ควรสังเกตว่าการมองหาคุณลักษณะบางอย่างในห่วงโซ่ต้นแบบมีผลกระทบต่อประสิทธิภาพ ยิ่งระดับวัตถุต้นแบบที่คุณต้องการสูงขึ้นเท่าใดก็ยิ่งส่งผลกระทบต่อประสิทธิภาพมากขึ้นเท่านั้น หากคุณมองหาคุณสมบัติที่ไม่มีอยู่จริงมันจะสำรวจห่วงโซ่ต้นแบบทั้งหมด
การกระทำนี้ชี้
ไม่ว่าจะกำหนดไว้ที่ไหนเมื่อใช้มันจะชี้ไปที่วัตถุปัจจุบันเสมอไม่ใช่วัตถุต้นแบบ
var o = {a: 2, m: ฟังก์ชัน (b) {return this.a + 1; }}; var p = object.create (o); pa = 12; pm () // 13ในรหัสข้างต้นวิธี M ของวัตถุ P มาจากวัตถุต้นแบบ o ในเวลานี้วัตถุนี้ภายในวิธี M ไม่ได้ชี้ไปที่ O แต่เป็น p
มรดกของตัวสร้าง
ส่วนนี้แนะนำวิธีการสร้างตัวสร้างหนึ่งมรดกตัวสร้างอื่น
สมมติว่ามีตัวสร้างรูปร่าง
function shape () {this.x = 0; this.y = 0;} shape.prototype.move = function (x, y) {this.x += x; this.y += y; console.info ('รูปร่างย้าย');}; ตัวสร้างสี่เหลี่ยมผืนผ้าสืบทอดรูปร่าง ฟังก์ชั่นสี่เหลี่ยมผืนผ้า () {shape.call (นี่); // เรียกตัวสร้างคลาสแม่} // อีกวิธีในการเขียนสี่เหลี่ยมฟังก์ชัน () {this.base = shape; this.base ();} // subclass สืบทอดวิธีการคลาสแม่แบบ rectangle.prototype = object.create (รูปร่าง. prototype); rect.prototype.constructor = rectangle; var rect = new ectangle ()รหัสด้านบนแสดงให้เห็นว่าการสืบทอดของตัวสร้างถูกแบ่งออกเป็นสองส่วนหนึ่งคือ subclass เรียกวิธีการสร้างของคลาสแม่และอีกส่วนหนึ่งคือต้นแบบของจุดย่อยไปยังต้นแบบของคลาสแม่
ในรหัสข้างต้น subclass สืบทอดคลาสพาเรนต์โดยรวม บางครั้งจำเป็นต้องใช้วิธีการสืบทอดของวิธีเดียวและสามารถใช้วิธีการเขียนต่อไปนี้ได้
classb.prototype.print = function () {classa.prototype.print.call (นี่); // บางรหัส}ในรหัสข้างต้นวิธีการพิมพ์ของคลาสย่อย B เรียกวิธีการพิมพ์ของคลาสแม่คลาส A แล้วปรับใช้รหัสของตัวเอง สิ่งนี้เทียบเท่ากับการสืบทอดวิธีการพิมพ์ของคลาสแม่ A.
คุณลักษณะ __proto__
แอตทริบิวต์ __proto__ ชี้ไปที่วัตถุต้นแบบของวัตถุปัจจุบันนั่นคือแอตทริบิวต์ต้นแบบของตัวสร้าง
var obj = วัตถุใหม่ (); obj .__ proto__ === object.prototype // trueobj .__ proto__ === obj.constructor.prototype // true
รหัสข้างต้นแรกสร้างวัตถุใหม่ OBJ ซึ่งเป็นแอตทริบิวต์ __proto__ ซึ่งชี้ไปที่แอตทริบิวต์ต้นแบบของตัวสร้าง (Object หรือ OBJ.Constructor) ดังนั้นหลังจากเปรียบเทียบทั้งสองแล้วกลับมาจริง
ดังนั้นจึงมีสามวิธีในการรับวัตถุต้นแบบของวัตถุอินสแตนซ์ obj
ในสามวิธีข้างต้นสองวิธีแรกไม่น่าเชื่อถือมาก มาตรฐาน ES6 ล่าสุดกำหนดว่ามีเพียงเบราว์เซอร์เท่านั้นที่ต้องปรับใช้แอตทริบิวต์ __Proto__ และสภาพแวดล้อมอื่น ๆ อาจไม่สามารถปรับใช้ได้ อย่างไรก็ตาม obj.constructor.prototype อาจล้มเหลวเมื่อเปลี่ยนวัตถุต้นแบบด้วยตนเอง
var p = function () {}; var p = new p (); var c = function () {}; c.prototype = p; var c = new c (); c.constructor.prototype === p // falseในรหัสข้างต้นวัตถุต้นแบบของตัวสร้าง C จะเปลี่ยนเป็น P และผลลัพธ์คือ C.Constructor.prototype ถูกบิดเบือน ดังนั้นเมื่อเปลี่ยนวัตถุต้นแบบแอตทริบิวต์ตัวสร้างควรตั้งค่าในเวลาเดียวกัน
C.Prototype = P; C.Prototype.Constructor = C; C.Constructor.prototype === P // true
ดังนั้นจึงขอแนะนำให้ใช้ method.getPrototypeof เพื่อรับวัตถุต้นแบบ การใช้วิธีนี้มีดังนี้
var o = วัตถุใหม่ (); object.getPrototypeof (o) === Object.prototype // true
คุณสามารถใช้ method.getPrototypeof เพื่อตรวจสอบว่าเบราว์เซอร์รองรับแอตทริบิวต์ __Proto__ ซึ่งไม่ได้รับการสนับสนุนโดยเบราว์เซอร์เก่าหรือไม่
Object.getPrototypeof ({__proto__: null}) === nullรหัสด้านบนตั้งค่าแอตทริบิวต์ __proto__ ของวัตถุเป็นโมฆะจากนั้นใช้ method.getPrototypeof วิธีการรับต้นแบบของวัตถุนี้เพื่อตรวจสอบว่ามันเท่ากับค่าว่างหรือไม่ หากสภาพแวดล้อมปัจจุบันรองรับแอตทริบิวต์ __proto__ ผลการเปรียบเทียบของทั้งสองควรเป็นจริง
ด้วยแอตทริบิวต์ __proto__ มันเป็นเรื่องง่ายที่จะตั้งค่าต้นแบบของวัตถุอินสแตนซ์ สมมติว่ามีวัตถุสามอย่าง ได้แก่ เครื่องจักรยานพาหนะและรถยนต์ซึ่งเครื่องเป็นต้นแบบของยานพาหนะและยานพาหนะเป็นต้นแบบของรถยนต์ซึ่งสามารถตั้งค่าได้ด้วยรหัสเพียงสองบรรทัด
ยานพาหนะ. __ proto__ = เครื่องจักร; car .__ proto__ = ยานพาหนะ;
ต่อไปนี้เป็นตัวอย่าง คุณสมบัติที่กำหนดไว้ในวัตถุต้นแบบจะถูกอ่านตามลำดับผ่านแอตทริบิวต์ __Proto__ และแอตทริบิวต์ constructor.prototype
array.prototype.p = 'abc'; var a = new Array (); a .__ proto __. p // abca.constructor.prototype.p // abc
เห็นได้ชัดว่า __proto__ ดูกระชับขึ้นอีกเล็กน้อย
เมื่อวัตถุอินสแตนซ์ถูกสร้างขึ้นผ่านตัวสร้างแอตทริบิวต์ __proto__ ของวัตถุอินสแตนซ์จะชี้ไปที่วัตถุต้นแบบของตัวสร้างโดยอัตโนมัติ
var f = function () {}; var a = {}; f.prototype = a; var o = new f (); o .__ proto__ === a // trueมรดกของคุณลักษณะ
มีคุณลักษณะสองประเภท หนึ่งคือคุณลักษณะดั้งเดิมของวัตถุเองและอีกอย่างคือแอตทริบิวต์ที่สืบทอดมาจากต้นแบบ
คุณสมบัติดั้งเดิมของวัตถุ
คุณสมบัติทั้งหมดของวัตถุนั้นสามารถรับได้โดยใช้วิธีการ Object.GetOwnPropertyNames
Object.getOwnPropertyNames (วันที่) // ["parse", "อาร์กิวเมนต์", "UTC", "Caller", "Name", "Prototype", "Now", "Length"]
ในบรรดาคุณสมบัติของวัตถุเองบางตัวมีความสามารถในการจอง (ที่ระบุได้) ในขณะที่คนอื่น ๆ ไม่สามารถระบุได้ รับคุณสมบัติเหล่านั้นที่สามารถแจกแจงได้เท่านั้นใช้วิธีการ Object.keys
Object.keys (วันที่) // [] HasownProperty ()
วิธีการ HasownProperty ส่งคืนค่าบูลีนเพื่อตรวจสอบว่าคุณสมบัติบางอย่างถูกกำหนดไว้ในวัตถุเองหรือบนห่วงโซ่ต้นแบบ
date.hasownproperty ('length') // truedate.hasownproperty ('toString') // falseวิธีการ HasownProperty เป็นวิธีเดียวใน JavaScript ที่ไม่ผ่านห่วงโซ่ต้นแบบเมื่อประมวลผลคุณสมบัติของวัตถุ
คุณสมบัติการสืบทอดของวัตถุ
วัตถุที่สร้างขึ้นด้วยวิธีการสร้างวัตถุจะสืบทอดคุณสมบัติของวัตถุต้นแบบทั้งหมด
var proto = {p1: 123}; var o = object.create (proto); o.p1 // 123o.hasownproperty ("p1") // falseรับคุณลักษณะทั้งหมด
เพื่อตรวจสอบว่าวัตถุมีคุณสมบัติบางอย่าง (ไม่ว่าจะเป็นของตัวเองหรือสืบทอด) ให้ใช้ตัวดำเนินการใน
"ความยาว" ในวันที่ // true "toString" ในวันที่ // true
รับคุณสมบัติทั้งหมดของวัตถุ (ไม่ว่าจะเป็นของตัวเองหรือสืบทอด) และคุณสามารถใช้ลูปสำหรับสำหรับ
var o1 = {p1: 123}; var o2 = object.create (o1, {p2: {value: "abc", enumerable: true}}); สำหรับ (p ใน o2) {console.info (p);} // p2 // p1 // p1เพื่อที่จะได้รับคุณสมบัติของวัตถุในสำหรับ ... ในลูปคุณสามารถใช้วิธีการ HasownProperty เพื่อตัดสิน
สำหรับ (ชื่อ var ในวัตถุ) {if (object.hasownproperty (ชื่อ)) { / * รหัสลูป * /}}}เพื่อให้ได้คุณสมบัติทั้งหมดของวัตถุ (ไม่ว่าจะเป็นของตัวเองหรือสืบทอดและไม่ว่าจะเป็นสิ่งที่สามารถระบุได้) คุณสามารถใช้ฟังก์ชั่นต่อไปนี้
ฟังก์ชั่นสืบต่อที่สืบทอดมาจากชื่อ (obj) {var props = {}; ในขณะที่ (obj) {object.getOwnPropertyNames (OBJ) .Foreach (ฟังก์ชั่น (p) {props [p] = true;}); obj = object.getPrototypeof (OBJ); } return object.getownpropertyNames (อุปกรณ์ประกอบฉาก);}การใช้งานมีดังนี้:
ชื่อที่สืบทอดมาจากชื่อ (วันที่) // ["ผู้โทร", "คอนสตรัคเตอร์", "ทอร์ตริ่ง", "UTC", "เรียก", "แยกวิเคราะห์", "ต้นแบบ", "__definesetter__", __lookuptetter__ "," ความยาว "," "Propertyisenumerable", "valueof", "ใช้", "__definegetter__", "ชื่อ", "Now", "HasownProperty"]
สำเนาของวัตถุ
หากคุณต้องการคัดลอกวัตถุคุณต้องทำสองสิ่งต่อไปนี้
ตรวจสอบให้แน่ใจว่าวัตถุที่คัดลอกมีวัตถุต้นแบบเดียวกับวัตถุดั้งเดิม
ตรวจสอบให้แน่ใจว่าวัตถุที่คัดลอกมีคุณสมบัติเช่นเดียวกับวัตถุดั้งเดิม
ต่อไปนี้เป็นฟังก์ชั่นของการคัดลอกวัตถุที่เขียนตามสองจุดข้างต้น
ฟังก์ชั่น copyObject (orig) {var copy = object.create (Object.getPrototypeof (Orig)); CopyOwnPropertiesFrom (Copy, Orig); return copy;} function copyOwnPropertiesFrom (เป้าหมาย, แหล่งที่มา) {Object .GetOwnPropertyNames (แหล่งที่มา). foreach (ฟังก์ชั่น (propkey) {var desc = object.getOwnPropertyDescriptor (แหล่งที่มา, propkey); object.defineProperty คืนเป้าหมาย;}มรดกหลายอย่าง
JavaScript ไม่ได้มีฟังก์ชั่นการสืบทอดหลายอย่างนั่นคือไม่อนุญาตให้วัตถุหนึ่งวัตถุสืบทอดหลายวัตถุในเวลาเดียวกัน อย่างไรก็ตามฟังก์ชั่นนี้สามารถทำได้ผ่านการแก้ปัญหา
ฟังก์ชั่น m1 (prop) {this.hello = prop;} ฟังก์ชั่น m2 (prop) {this.world = prop;} ฟังก์ชั่น s (p1, p2) {this.base1 = m1; this.base1 (p1); this.base2 = m2; this.base2 (p2);} s.prototype = new m1 (); var s = new s (111, 222); s.hello // 111sworld // 222ในรหัสข้างต้น Subclass S สืบทอดทั้งคลาสแม่ M1 และ M2 แน่นอนจากมุมมองของห่วงโซ่การสืบทอด S มีเพียงหนึ่งพาเรนต์คลาส M1 แต่เนื่องจากในกรณีของ S ตัวสร้างของ M1 และ M2 จะถูกดำเนินการในเวลาเดียวกันมันสืบทอดวิธีการของสองคลาสนี้ในเวลาเดียวกัน