การทำงานของระบบของที่สืบทอด ECMAScript
- หน้าก่อนหน้า ตัวอย่างของระบบการสืบทอด
- หน้าต่อไป สารานุกรม JavaScript อันยอดเยี่ยม
การปฏิบัติระบบสืบทอด
เพื่อที่จะปฏิบัติกันระบบสืบทอดด้วย ECMAScript คุณสามารถเริ่มจากรูปแบบพื้นฐานที่ต้องการสืบทอด ทุกคลาสที่ถูกกำหนดโดยผู้พัฒนาสามารถใช้เป็นรูปแบบพื้นฐาน ด้วยเหตุผลด้านความปลอดภัย คลาสท้องถิ่นและคลาสเจ้าจัดการไม่สามารถใช้เป็นรูปแบบพื้นฐาน เพื่อป้องกันการเข้าถึงโค้ดที่บันทึกแล้วของบราวเซอร์ระดับสาธารณะ ซึ่งสามารถใช้ในการโจมตีร้ายแรง
หลังจากเลือกรูปแบบพื้นฐาน คุณสามารถสร้างลูกชั้นของมันได้ การใช้รูปแบบพื้นฐานนั้นขึ้นอยู่กับความตัดสินใจของคุณ บางครั้งคุณอาจต้องการที่จะสร้างรูปแบบพื้นฐานที่ไม่สามารถใช้โดยตรง ซึ่งเป็นวิธีที่ให้คุณสามารถใช้ฟังก์ชันทั่วไปแก่ลูกชั้น ในกรณีนี้ รูปแบบพื้นฐานถือว่าเป็นรูปแบบที่ไม่อนุญาตใช้
ถึงแม้ว่า ECMAScript จะไม่มีการกำหนดรูปแบบที่ไม่อนุญาตใช้เหมือนกับภาษาอื่น ๆ แต่บางครั้งมันก็สร้างรูปแบบที่ไม่อนุญาตใช้ โดยทั่วไปเรียกว่ารูปแบบที่ไม่อนุญาตใช้
การสร้างลูกชั้นจะสืบทอดทั้งคุณสมบัติและวิธีทั้งหมดของรูปแบบพ่อเหล็ก รวมถึงการปฏิบัติตัวแบบแบบรูปแบบและการปฏิบัติวิธี จำนนว่า ทุกคุณสมบัติและวิธีเป็นสาธารณะ ดังนั้นลูกชั้นสามารถเข้าถึงวิธีเหล่านั้นได้โดยตรง ลูกชั้นยังสามารถเพิ่มคุณสมบัติและวิธีใหม่ที่ไม่มีในรูปแบบพ่อเหล็ก หรือเป็นที่ทำนองคุณสมบัติและวิธีของรูปแบบพ่อเหล็ก
วิธีที่สืบทอด
เหมือนกับความสามารถอื่น การที่ ECMAScript จะทำการสืบทอดมีหลายวิธี นี่เพราะมีทางแสดงระบบสืบทอดใน JavaScript ไม่มีกฎระเบียบเฉพาะ แต่ที่มีทางแสดงโดยการโครงงาน นี่หมายความว่าข้อมูลทั้งหมดของระบบสืบทอดไม่ได้ทั้งหมดจัดการโดยโปรแกรมเก่า ในฐานะนักพัฒนา คุณมีสิทธิ์ที่จะเลือกวิธีที่เหมาะสมที่สุด
ในนี้ ผมจะเล่าให้คุณเห็นหลายวิธีที่สามารถสืบทอดได้
การปลอมตัว
เมื่อคิดค้น ECMAScript มันไม่มีเจตนาที่จะออกแบบการปลอมตัวเพื่อสืบทอด (object masquerading) มันเกิดขึ้นหลังจากที่นักพัฒนาเริ่มเข้าใจวิธีที่ฟังก์ชันทำงาน โดยเฉพาะอย่างยิ่ง การใช้คำสั่ง this ในสภาพความเป็นอยู่ของฟังก์ชัน
หลักการของมันคือ: การใช้คำสั่ง this ในเครื่องดูดแสงสร้างขึ้นทุกตัวแปรและวิธี (นั่นคือ ใช้วิธีสร้างเครื่องดูดแสงของคลาส) มีเพียงฟังก์ชันเดียว ดังนั้นเครื่องดูดแสง ClassA สามารถใช้เป็นวิธีของ ClassB และเรียกมัน จะมี ClassB จะได้ทุกตัวแปรและวิธีที่มีอยู่ในเครื่องดูดแสง ClassA ของเครื่องดูดแสง ตัวอย่างเช่น การกำหนด ClassA และ ClassB ดังต่อไป
function ClassA(sColor) { this.color = sColor; this.sayColor = function () { alert(this.color); }; } function ClassB(sColor) { }
คุณหรือระลึกไหม? ใครีย์ this จะหมายถึงตัวที่ถูกสร้างขึ้นโดยเครื่องดูดแสงในปัจจุบัน แต่ในกรณีนี้ this จะหมายถึงตัวที่ถูกติดต่อกับตัวนั้น นี่เป็นทฤษฎีที่ใช้ ClassA ทั้งเป็นฟังก์ชันปกติเพื่อสร้างมีทางแสดงมีทางแสดงเพื่อสืบทอด ไม่ใช่เพื่อใช้เป็นเครื่องดูดแสง อย่างเช่น การใช้เครื่องดูดแสง ClassB ตามต่อไปนี้สามารถสร้างมีทางแสดงได้
function ClassB(sColor) { this.newMethod = ClassA; this.newMethod(sColor); delete this.newMethod; }
ในโค้ดดังกล่าว สำหรับเรียกใช้วิธี newMethod ของ ClassA (โปรดจำไว้ว่า ชื่อฟังก์ชันเป็นบิตที่ชี้ที่ยังไม่มี) และใส่ของตัวที่ใช้สำหรับเครื่องดูดแสง sColor ของตัวก่อสร้าง ClassB ของแถวสุดท้ายในโค้ดลบการติดต่อเพื่อเรียกใช้ ClassA อีกครั้งต่อไป
ทุกตัวแปรและวิธีที่เป็นที่มีอยู่ต้องถูกประกาศด้วยโค้ดส่วนที่เพิ่มเติมหลังจากลบคำสั่งเพิ่มเติมวิธีสำหรับการลบ ไม่เช่นนั้น อาจจะลบทุกตัวแปรและวิธีที่เกี่ยวข้องของคลาสขึ้นไป
function ClassB(sColor, sName) { this.newMethod = ClassA; this.newMethod(sColor); delete this.newMethod; this.name = sName; this.sayName = function () { alert(this.name); }; }
เพื่อแสดงว่าโค้ดข้างต้นทำงานได้เหมือนที่คาดหวัง คุณสามารถปฏิบัติการต่อไปด้วยตัวอย่างดังต่อไป
var objA = new ClassA("blue"); var objB = new ClassB("red", "John"); objA.sayColor(); //ออก "blue" objB.sayColor(); //ออก "red" objB.sayName(); // ออก "John"
การปลอมตัวสามารถสืบทอดเหลืองหลายอันดับได้
ที่น่าสนใจคือ การปลอมตัวเพื่อสืบทอดจะสนับสนุนสืบทอดเหลืองคลาสหลายอันดับ หรือว่า คลาสหนึ่งสามารถสืบทอดหลายคลาสขึ้นไป มีทางแสดงระบบสืบทอดเหลืองหลายอันดับด้วย UML ตามที่มีทางแสดงด้านล่าง

เช่นเดียวกับ ถ้ามีสองคลาส ClassX และ ClassY และ ClassZ ต้องการที่จะสืบทอดคลาสเหล่านั้น คุณสามารถใช้โค้ดดังต่อไปนี้
function ClassZ() { this.newMethod = ClassX; this.newMethod(); delete this.newMethod; this.newMethod = ClassY; this.newMethod(); delete this.newMethod; }
มีข้อบกพร่องหนึ่งที่นี่ หากมีสองรูปแบบ ClassX และ ClassY ที่มีคุณสมบัติหรือฟังก์ชันที่มีชื่อเดียวกัน หรือมีความสำคัญที่สูงกว่า ClassY เพราะมันมาจากคลาสที่มีความสำคัญต่อมา นอกจากข้อบกพร่องนี้แล้ว การทำการประสานงานแบบ object composition ในระบบ multiple inheritance จะง่ายและไม่มีปัญหา
เนื่องจากความนิยมของวิธีนี้ ECMAScript รุ่นที่สามได้เพิ่มฟังก์ชันสองตัวเข้าไปในอ็จอมเป้าหมาย Function คือ call() และ apply()
call() ฟังก์ชัน
call() ฟังก์ชันเป็นที่เคียงของวิธีประสานงานอ็จอมเป้าหมายโบราณที่สุด ตัวแปรแรกใช้เป็นอนุตาญาะของ this ตัวแปรที่เหลือส่งผ่านตรงไปยังฟังก์ชันตัวเอง ตัวอย่าง:
function sayColor(sPrefix,sSuffix) { alert(sPrefix + this.color + sSuffix); }; var obj = new Object(); obj.color = "blue"; sayColor.call(obj, "The color is ", "a very nice color indeed.");
ในตัวอย่างนี้ ฟังก์ชัน sayColor() ถูกกำหนดนอกอ็จอมเป้าหมาย แม้ว่ามันไม่เป็นส่วนของอ็จอมเป้าหมายใดๆ ก็ตาม ยังสามารถใช้คำกำหนด this ได้ อาทิ คุณสมบัติ color ของอ็จอมเป้าหมาย obj มีค่า blue การเรียกใช้ฟังก์ชัน call() มีตัวแปรแรกคือ obj หมายความว่าควรกำหนดค่าของ this ในฟังก์ชัน sayColor() คือ obj ตัวแปรที่สองและสามคือตัวแปรของข้อความ ซึ่งตรงกันกับตัวแปร sPrefix และ sSuffix ของฟังก์ชัน sayColor() และข้อความที่สร้างขึ้นเรียกว่า "The color is blue, a very nice color indeed." จะถูกแสดงออกมา
เพื่อใช้กับวิธีที่มีส่วนร่วมกับวิธีประสานงานของมาทราย มาเทรียบเทียบเพียงแค่เปลี่ยนรายการการเข้าใจ การเรียกใช้และการลบรายการที่มีอยู่สามรายการเท่านั้น:
function ClassB(sColor, sName) { //this.newMethod = ClassA; //this.newMethod(color); //delete this.newMethod; ClassA.call(this, sColor); this.name = sName; this.sayName = function () { alert(this.name); }; }
ที่นี่ เราต้องการให้คำกำหนดที่เรียกว่า this ใน ClassA ตรงกันข้ามกับอ็จอมเป้าหมายที่สร้างใหม่ ClassB ดังนั้น this จึงเป็นตัวแปรแรกสุด ตัวแปรที่สอง sColor คือตัวแปรที่เป็นเดียวกันสำหรับทั้งสองรูปแบบของคลาส
apply() ฟังก์ชัน
apply() มีสองตัวแปร ใช้เป็นอนุตาญาะของ this และตัวแปรของอารยะที่จะส่งผ่านเข้าไปในฟังก์ชัน ตัวอย่าง:
function sayColor(sPrefix,sSuffix) { alert(sPrefix + this.color + sSuffix); }; var obj = new Object(); obj.color = "blue"; sayColor.apply(obj, new Array("The color is ", "a very nice color indeed."));
ตัวอย่างนี้เหมือนตัวอย่างก่อนหน้านี้ แต่ตอนนี้เรียกด้วยวิธี apply() ในการเรียกวิธี apply() พารามิเตอร์แรกก็คือ obj ซึ่งกล่าวว่าควรจะสร้างค่าของ this ในฟังก์ชัน sayColor() คือ obj พารามิเตอร์ที่สองคือแบบภาพที่มีสองตัวแปลตรงกันข้ามกับพารามิเตอร์ sPrefix และ sSuffix ของฟังก์ชัน sayColor() และข้อความที่สร้างขึ้นใหม่ก็คือ "The color is blue, a very nice color indeed." จะถูกแสดงออกมา
วิธีนี้ยังใช้เพื่อที่จะทดแทนการจัดการสัญญาณ การเรียกใช้และการลบวิธีใหม่ที่มีตัวอย่างข้างต้น
function ClassB(sColor, sName) { //this.newMethod = ClassA; //this.newMethod(color); //delete this.newMethod; ClassA.apply(this, new Array(sColor)); this.name = sName; this.sayName = function () { alert(this.name); }; }
เช่นเดียวกัน พารามิเตอร์แรกก็คือ this พารามิเตอร์ที่สองคือแบบภาพที่มีค่าเพียงหนึ่งเพื่อค่า color สามารถส่งทั้งโอปเจ็กต์ arguments ทั้งหมดของ ClassB ไปยังวิธี apply() ด้วย
function ClassB(sColor, sName) { //this.newMethod = ClassA; //this.newMethod(color); //delete this.newMethod; ClassA.apply(this, arguments); this.name = sName; this.sayName = function () { alert(this.name); }; }
ทั้งนี้ มีเพียงแค่ของที่มีลำดับของพารามิเตอร์ในรายการของหลักเชื้อเลือดเป็นลำดับที่ตรงกันข้ามกับลำดับของพารามิเตอร์ในรายการของหลักลูกเท่านั้นที่สามารถส่งผ่านองค์ประกอบพารามิเตอร์ ถ้าไม่เช่นนั้น จะต้องสร้างแบบภาพเพิ่มเติมเพื่อที่จะจัดลำดับของพารามิเตอร์ในลำดับที่ถูกต้อง นอกจากนี้ ยังสามารถใช้วิธี call() ด้วย
โปรโตไทป์เชนช์ (prototype chaining)
รูปแบบการสืบทอดนี้ใช้ใน ECMAScript สำหรับโปรโตไทป์เชนช์ ในบทก่อนหน้านี้ได้นำเสนอวิธีกำหนดโปรโตไทป์ของชั้น โปรโตไทป์เชนช์ขยายวิธีนี้ด้วยวิธีที่มีความน่าสนใจเพื่อปฏิบัติการระบบสืบทอด
ทั้งนี้ หลังจากที่เรียกด้วยตัวอย่างในบทก่อนหน้านี้ โปรโตไทป์ (prototype) คือแม่แบบ โดยที่ทุกตัวแทนที่ต้องการที่จะเป็นตัวแทนของโปรโตไทป์นั้นจะมีฐานของแม่แบบนี้ ซึ่งทั้งหมดของคุณสมบัติและวิธีของโปรโตไทป์ที่ส่งผ่านให้กับทุกตัวแทนของชั้นนี้ โปรโตไทป์เชนช์นำหน้านี้ใช้ฟีเจอร์นี้เพื่อปฏิบัติการรวมชุดของระบบจำเป็นต่อการสืบทอด
ถ้าใช้วิธีโปรโตไทป์เพื่อกำหนดค่าใหม่ให้กับชั้นที่ได้เรียกด้วยตัวอย่างก่อนหน้านี้ พวกเขาจะกลายเป็นรูปแบบดังนี้:
function ClassA() { } ClassA.prototype.color = "blue"; ClassA.prototype.sayColor = function () { alert(this.color); }; function ClassB() { } ClassB.prototype = new ClassA();
ประสิทธิภาพของวิธีโปรโตไทป์น่าทึ่งนั้นนั่นคือบรรทัดที่มีสีน้ำเงินที่มีตัวอย่าง ที่นี่ การตั้งค่าโปรโตไทป์ของ ClassB กับตัวอย่างของ ClassA นั้นมีน่าสนใจ เพราะต้องการที่จะได้รับคุณสมบัติและฟังก์ชันของ ClassA ทั้งหมด แต่ไม่ต้องเพิ่มเข้าไปที่โปรโตไทป์ของ ClassB มีวิธีที่ดีกว่านี้หรือไม่
จำเป็น:โรงสร้าง ClassA โดยไม่ส่งค่าเข้าให้กับตัวแปร นี่เป็นกระบวนการที่มีอยู่ในโปรโตไทป์แชนน์ ต้องการให้โรงสร้างไม่มีตัวแปร
ที่คล้ายกับการปลอมตัวโอบเจก ทุกคุณสมบัติและฟังก์ชันของชั้นย่อยต้องปรากฏอยู่หลังจากการกำหนดค่าให้กับโปรโตไทป์แบบลอง สาเหตุที่ทำให้เกิดเช่นนี้คือเพราะโปรโตไทป์แบบลองถูกแทนที่ด้วยโอบเจกใหม่ โอบเจกที่เพิ่มฟังก์ชันใหม่ดังกล่าวจะถูกทำลาย ดังนั้น รหัสเพื่อเพิ่มคุณสมบัติ name และฟังก์ชัน sayName() ของ ClassB จะดังนี้
function ClassB() { } ClassB.prototype = new ClassA(); ClassB.prototype.name = ""; ClassB.prototype.sayName = function () { alert(this.name); };
สามารถทดสอบรหัสดังนี้เพื่อตรวจสอบรหัสนี้
var objA = new ClassA(); var objB = new ClassB(); objA.color = "blue"; objB.color = "red"; objB.name = "John"; objA.sayColor(); objB.sayColor(); objB.sayName();
นอกจากนี้ ในโปรโตไทป์แชนน์ การทำงานของการคำนึงของอินสแตนซ์ออฟแบล็คเกิลด้วยเช่นกัน ด้วยตัวอย่างที่อาจจะเห็น ทุกตัวอย่างของ ClassB จะคำนึงอินสแตนซ์ออฟ ClassA และ ClassB ทั้งหมดให้กลับค่า "true"
var objB = new ClassB(); alert(objB instanceof ClassA); //ออก "true" alert(objB instanceof ClassB); //ออก "true"
ในโลกของ ECMAScript ที่มีชนิดของประเภทที่อ่อน นี่เป็นเครื่องมือที่มีประโยชน์มาก แต่ไม่สามารถใช้มันได้เมื่อใช้การปลอมตัวโอบเจก
ข้อจำกัดของโปรโตไทป์แชนน์คือไม่สนับสนุนการสืบทอดเชิงหลายเชื่อม จำนวน โปรโตไทป์แชนน์จะใช้โอบเจกของประเภทอื่นเขียนทับค่าของโปรโตไทป์ของชั้น
วิธีผสม
วิธีที่มีอยู่เพื่อสืบทอดคือการกำหนดชั้นโดยใช้ฟังก์ชันวิเคราะห์ ไม่ใช้สำหรับใดๆ ของโปรโตไทป์ ปัญหาหลักของการปลอมตัวโอบเจกคือต้องใช้วิธีวิเคราะห์ฟังก์ชัน นี่ไม่ใช่ทางเลือกที่ดีที่สุด อย่างไรก็ตาม ถ้าใช้โปรโตไทป์แชนน์ จะไม่สามารถใช้ฟังก์ชันวิเคราะห์ที่มีตัวแปรต่างๆ ได้ นักพัฒนาจะเลือกอย่างไร? ตอบได้ง่าย ทั้งสองตัวทั้งหมด
ในบทก่อนหน้านี้ เราได้มีการอธิบายถึงวิธีที่ดีที่สุดในการสร้าง class คือ ใช้ constructor ที่กำหนดคุณสมบัติ และใช้ prototype ที่กำหนดวิธี วิธีนี้มีความเหมาะสมกับระบบการสืบทอด ด้วยการใช้ object ในการสืบทอดคุณสมบัติของ constructor และใช้ prototype chain ในการสืบทอดหน้าตรงของ prototype object ด้วยทั้งสองวิธีนี้ สามารถเขียนตัวอย่างดังนี้
function ClassA(sColor) { this.color = sColor; } ClassA.prototype.sayColor = function () { alert(this.color); }; function ClassB(sColor, sName) { ClassA.call(this, sColor); this.name = sName; } ClassB.prototype = new ClassA(); ClassB.prototype.sayName = function () { alert(this.name); };
ในตัวอย่างนี้ ระบบการสืบทอดที่ใช้โดยสองบรรทัดแสดงด้วยสีส้ม โดยในบรรทัดแสดงด้วยสีส้มแรก ใช้ object ในการสืบทอดคุณสมบัติ sColor ของ ClassA ใน constructor ของ ClassB ในบรรทัดแสดงด้วยสีส้มที่สอง ใช้ prototype chain ในการสืบทอดของหน้าตรงของ ClassA มีทั้งหมด ด้วยเหตุผลที่ใช้ prototype chain ทั้งสองวิธีนี้ ตัวบวก instanceof ยังสามารถทำงานได้อย่างถูกต้อง
ตัวอย่างด้านล่างนี้ทดสอบรหัสนี้:
var objA = new ClassA("blue"); var objB = new ClassB("red", "John"); objA.sayColor(); //ออก "blue" objB.sayColor(); //ออก "red" objB.sayName(); //ออก "John"
- หน้าก่อนหน้า ตัวอย่างของระบบการสืบทอด
- หน้าต่อไป สารานุกรม JavaScript อันยอดเยี่ยม