ใน JavaScript เราสามารถสืบทอดได้จากวัตถุเดียวเท่านั้น สามารถมีได้เพียงหนึ่ง [[Prototype]]
สำหรับวัตถุ และคลาสหนึ่งอาจขยายคลาสอื่นได้เพียงคลาสเดียวเท่านั้น
แต่บางครั้งมันก็รู้สึกมีข้อจำกัด ตัวอย่างเช่น เรามีคลาส StreetSweeper
และคลาส Bicycle
และต้องการผสมเข้าด้วยกัน: StreetSweepingBicycle
หรือเรามีคลาส User
และคลาส EventEmitter
ที่ใช้การสร้างเหตุการณ์ และเราต้องการเพิ่มฟังก์ชันการทำงานของ EventEmitter
ให้กับ User
เพื่อให้ผู้ใช้ของเราสามารถปล่อยเหตุการณ์ได้
มีแนวคิดที่สามารถช่วยได้ที่นี่เรียกว่า "มิกซ์อิน"
ตามที่กำหนดไว้ในวิกิพีเดีย มิกซ์อินคือคลาสที่มีวิธีการที่คลาสอื่นสามารถใช้ได้โดยไม่จำเป็นต้องสืบทอดจากคลาสนั้น
กล่าวอีกนัยหนึ่ง mixin จัดเตรียมวิธีการที่ใช้พฤติกรรมบางอย่าง แต่เราไม่ได้ใช้มันเพียงอย่างเดียว เราใช้มันเพื่อเพิ่มพฤติกรรมให้กับคลาสอื่น ๆ
วิธีที่ง่ายที่สุดในการใช้มิกซ์อินใน JavaScript คือการสร้างออบเจ็กต์ด้วยวิธีการที่มีประโยชน์ เพื่อให้เราสามารถรวมมันเข้ากับต้นแบบของคลาสใดก็ได้ได้อย่างง่ายดาย
ตัวอย่างเช่นที่นี่ mixin sayHiMixin
ใช้เพื่อเพิ่ม "คำพูด" สำหรับ User
:
//มิกซ์อิน ให้ sayHiMixin = { พูดสวัสดี() { alert(`สวัสดี ${this.name}`); - บอกลา() { alert(`ลาก่อน ${this.name}`); - - // การใช้งาน: ผู้ใช้คลาส { ตัวสร้าง (ชื่อ) { this.name = ชื่อ; - - // คัดลอกวิธีการ Object.มอบหมาย (User.prototype, sayHiMixin); // ตอนนี้ผู้ใช้สามารถทักทายได้ ผู้ใช้ใหม่("เพื่อน").sayHi(); //สวัสดีเพื่อน!
ไม่มีการสืบทอด แต่มีวิธีการคัดลอกแบบง่ายๆ ดังนั้น User
อาจสืบทอดมาจากคลาสอื่น และยังรวม mixin เข้ากับ "mix-in" วิธีการเพิ่มเติม เช่นนี้
ผู้ใช้คลาสขยายบุคคล { - - Object.มอบหมาย (User.prototype, sayHiMixin);
Mixins สามารถใช้ประโยชน์จากมรดกภายในตัวมันเองได้
ตัวอย่างเช่น ที่นี่ sayHiMixin
สืบทอดมาจาก sayMixin
:
ให้บอกว่าMixin = { พูด (วลี) { การแจ้งเตือน(วลี); - - ให้ sayHiMixin = { __proto__: sayMixin, // (หรือเราสามารถใช้ Object.setPrototypeOf เพื่อตั้งค่าต้นแบบที่นี่) พูดสวัสดี() { // เรียกวิธีหลัก super.say(`สวัสดี ${this.name}`); - - บอกลา() { super.say(`บาย ${this.name}`); - - - ผู้ใช้คลาส { ตัวสร้าง (ชื่อ) { this.name = ชื่อ; - - // คัดลอกวิธีการ Object.มอบหมาย (User.prototype, sayHiMixin); // ตอนนี้ผู้ใช้สามารถทักทายได้ ผู้ใช้ใหม่("เพื่อน").sayHi(); //สวัสดีเพื่อน!
โปรดทราบว่าการเรียกเมธอดพาเรนต์ super.say()
จาก sayHiMixin
(ที่บรรทัดที่มีป้ายกำกับ (*)
) จะค้นหาเมธอดในต้นแบบของมิกซ์อินนั้น ไม่ใช่คลาส
นี่คือแผนภาพ (ดูส่วนที่ถูกต้อง):
นั่นเป็นเพราะว่าวิธีการ sayHi
และ sayBye
ถูกสร้างขึ้นครั้งแรกใน sayHiMixin
ดังนั้นแม้ว่าพวกเขาจะถูกคัดลอก แต่ [[HomeObject]]
การอ้างอิงคุณสมบัติภายในของพวกเขา sayHiMixin
ดังที่แสดงในภาพด้านบน
เมื่อ super
ค้นหาวิธีการหลักใน [[HomeObject]].[[Prototype]]
นั่นหมายความว่ามันจะค้นหา sayHiMixin.[[Prototype]]
ตอนนี้เรามาสร้างมิกซ์อินเพื่อชีวิตจริงกันดีกว่า
คุณลักษณะที่สำคัญของออบเจ็กต์เบราว์เซอร์จำนวนมาก (เช่น) คือสามารถสร้างกิจกรรมได้ กิจกรรมเป็นวิธีที่ดีในการ “เผยแพร่ข้อมูล” ให้กับทุกคนที่ต้องการ ดังนั้นเรามาสร้างมิกซ์อินที่ช่วยให้เราสามารถเพิ่มฟังก์ชันที่เกี่ยวข้องกับเหตุการณ์ให้กับคลาส/อ็อบเจ็กต์ใดๆ ได้อย่างง่ายดาย
Mixin จะให้วิธีการ .trigger(name, [...data])
เพื่อ “สร้างเหตุการณ์” เมื่อมีบางสิ่งที่สำคัญเกิดขึ้น อาร์กิวเมนต์ name
คือชื่อของเหตุการณ์ ซึ่งอาจตามด้วยอาร์กิวเมนต์เพิ่มเติมพร้อมข้อมูลเหตุการณ์หรือไม่ก็ได้
นอกจากนี้ ยังมีเมธอด .on(name, handler)
ที่เพิ่มฟังก์ชัน handler
เป็นผู้ฟังเหตุการณ์ตามชื่อที่กำหนด มันจะถูกเรียกเมื่อเหตุการณ์ที่มี name
ที่กำหนดทริกเกอร์ และรับข้อโต้แย้งจากการเรียก .trigger
…และวิธีการ .off(name, handler)
ที่ลบ handler
Listener
หลังจากเพิ่มมิกซ์อิน user
อ็อบเจ็กต์จะสามารถสร้างเหตุการณ์ "login"
เมื่อผู้เยี่ยมชมเข้าสู่ระบบ และวัตถุอื่น เช่น calendar
อาจต้องการฟังเหตุการณ์ดังกล่าวเพื่อโหลดปฏิทินสำหรับผู้ที่เข้าสู่ระบบ
หรือ menu
สามารถสร้างเหตุการณ์ "select"
เมื่อรายการเมนูถูกเลือก และวัตถุอื่น ๆ อาจกำหนดตัวจัดการให้ตอบสนองต่อเหตุการณ์นั้น และอื่นๆ
นี่คือรหัส:
ให้ eventMixin = { - * สมัครสมาชิกกิจกรรมการใช้งาน: * menu.on('select', function(item) { ... } - บน (ชื่อเหตุการณ์, ตัวจัดการ) { ถ้า (!this._eventHandlers) this._eventHandlers = {}; ถ้า (!this._eventHandlers[ชื่อเหตุการณ์]) { this._eventHandlers[ชื่อเหตุการณ์] = []; - this._eventHandlers [ชื่อเหตุการณ์] .push (ตัวจัดการ); - - * ยกเลิกการสมัครสมาชิกการใช้งาน: * menu.off ('เลือก', ตัวจัดการ) - ปิด (ชื่อเหตุการณ์, ตัวจัดการ) { ให้ตัวจัดการ = this._eventHandlers?.[eventName]; ถ้า (!handlers) กลับมา; สำหรับ (ให้ i = 0; i < handlers.length; i++) { ถ้า (ตัวจัดการ [i] === ตัวจัดการ) { ตัวจัดการ splice (i--, 1); - - - - * สร้างกิจกรรมด้วยชื่อและข้อมูลที่กำหนด * this.trigger('เลือก', data1, data2); - ทริกเกอร์ (ชื่อเหตุการณ์ ... args) { ถ้า (!this._eventHandlers?.[ชื่อเหตุการณ์]) { กลับ; // ไม่มีตัวจัดการสำหรับชื่อเหตุการณ์นั้น - //เรียกผู้ดูแล this._eventHandlers [eventName] .forEach (ตัวจัดการ => handler.apply (สิ่งนี้, args)); - -
.on(eventName, handler)
– กำหนด handler
ฟังก์ชันให้ทำงานเมื่อมีเหตุการณ์ที่มีชื่อนั้นเกิดขึ้น ในทางเทคนิคแล้ว มีคุณสมบัติ _eventHandlers
ที่เก็บอาร์เรย์ของตัวจัดการสำหรับชื่อเหตุการณ์แต่ละชื่อ และเพียงเพิ่มเข้าไปในรายการ
.off(eventName, handler)
– ลบฟังก์ชันออกจากรายการตัวจัดการ
.trigger(eventName, ...args)
– สร้างเหตุการณ์: ตัวจัดการทั้งหมดจาก _eventHandlers[eventName]
จะถูกเรียก พร้อมด้วยรายการอาร์กิวเมนต์ ...args
การใช้งาน:
//จัดคลาส เมนูคลาส { เลือก (ค่า) { this.trigger("เลือก", ค่า); - - // เพิ่มมิกซ์อินด้วยวิธีที่เกี่ยวข้องกับเหตุการณ์ Object.มอบหมาย (เมนู.ต้นแบบ, eventMixin); ให้เมนู = เมนูใหม่ (); // เพิ่มตัวจัดการที่จะเรียกเมื่อเลือก: menu.on("select", value => alert(`ค่าที่เลือก: ${value}`)); // ทริกเกอร์เหตุการณ์ => ตัวจัดการด้านบนทำงานและแสดง: // ค่าที่เลือก: 123 เมนู.เลือก("123");
ตอนนี้ ถ้าเราต้องการให้โค้ดใด ๆ ตอบสนองต่อการเลือกเมนู เราสามารถฟังมันได้ด้วย menu.on(...)
และ eventMixin
mixin ทำให้การเพิ่มลักษณะการทำงานดังกล่าวให้กับคลาสได้มากเท่าที่เราต้องการเป็นเรื่องง่าย โดยไม่รบกวนสายโซ่การสืบทอด
Mixin – เป็นคำศัพท์การเขียนโปรแกรมเชิงวัตถุทั่วไป: คลาสที่มีวิธีการสำหรับคลาสอื่น
ภาษาอื่นบางภาษาอนุญาตให้มีการสืบทอดได้หลายครั้ง JavaScript ไม่รองรับการสืบทอดหลายรายการ แต่มิกซ์อินสามารถใช้งานได้โดยการคัดลอกวิธีการลงในต้นแบบ
เราสามารถใช้มิกซ์อินเพื่อเพิ่มคลาสโดยการเพิ่มพฤติกรรมหลายอย่าง เช่น การจัดการเหตุการณ์ ดังที่เราได้เห็นข้างต้น
Mixins อาจกลายเป็นจุดที่ขัดแย้งกันหากพวกเขาเขียนทับวิธีการเรียนที่มีอยู่โดยไม่ตั้งใจ โดยทั่วไปแล้วเราควรคิดให้ดีเกี่ยวกับวิธีการตั้งชื่อของมิกซ์อิน เพื่อลดความน่าจะเป็นที่จะเกิดขึ้น