در جاوااسکریپت ما فقط میتوانیم از یک شیء ارثبری کنیم. فقط یک [[Prototype]] برای هر شیء میتواند وجود داشته باشد. و یک کلاس فقط میتواند یک کلاس دیگر را تعمیم دهد.
اما گاهی اوقات این حس محدود بودن را دارد. برای مثال، ما کلاس StreetSweeper و کلاس Bicycle را داریم و میخواهیم ترکیب آنها را بسازیم: یک StreetSweepingBicycle.
یا ما کلاس User و کلاس EventEmitter که پیادهسازی ایجاد رویداد (event) انجام میدهد را داریم و میخواهیم که عملکرد EventEmitter را به User اضافه کنیم تا کاربران ما بتوانند رویدادها را خارج کنند.
یک راهکار وجود دارد که اینجا به کمک میآید، به نام “mixins”.
همانطور که در ویکیپدیا تعریف شده است، یک mixin کلاسی شامل متدهایی است که میتوانند بدون نیاز به ارثبری از کلاس، توسط کلاسهای دیگر استفاده شوند.
به عبارتی دیگر، یک mixin متدهایی که یک کار مشخص انجام میدهند را فراهم میکند اما از آن به تنهایی استفاده نمیکنیم بلکه از آن برای اضافه کردن همان کار مشخص به کلاسهای دیگر استفاده میکنیم.
یک مثال mixin
سادهترین راه برای پیادهسازی یک mixin در جاوااسکریپت ایجاد شیءای شامل متدهایی مفید است تا بتوانیم به راحتی آنها را درون پروتوتایپ هر کلاسی ادغام کنیم.
برای مثال اینجا میکسین sayHiMixin برای اضافه کردن «گفتار» به User استفاده شده است:
// mixin
let sayHiMixin = {
sayHi() {
alert(`Hello ${this.name}`);
},
sayBye() {
alert(`Bye ${this.name}`);
}
};
// :کاربرد
class User {
constructor(name) {
this.name = name;
}
}
// کپی کردن متدها
Object.assign(User.prototype, sayHiMixin);
// بگوید (hi) میتواند سلام User حالا
new User("Dude").sayHi(); // Hello Dude!
ارثبری در کار نیست، فقط یک کپی کردن متد ساده است. پس User میتواند از کلاس دیگری ارثبری کند و همچنین mixin را هم شامل شود تا متدهای اضافی را «ترکیب» کند، مثل این:
class User extends Person {
// ...
}
Object.assign(User.prototype, sayHiMixin);
mixinها میتوانند درون خود از ارثبری استفاده کنند.
برای مثال، اینجا sayHiMixin از sayMixin ارثبری میکند:
let sayMixin = {
say(phrase) {
alert(phrase);
}
};
let sayHiMixin = {
__proto__: sayMixin, // (برای تنظیم پروتوتایپ استفاده کنیم `Object.setPrototypeOf` یا میتوانستیم اینجا از)
sayHi() {
// فراخوانی متد والد
super.say(`Hello ${this.name}`); // (*)
},
sayBye() {
super.say(`Bye ${this.name}`); // (*)
}
};
class User {
constructor(name) {
this.name = name;
}
}
// کپی کردن متدها
Object.assign(User.prototype, sayHiMixin);
// بگوید (hi) میتواند سلام User حالا
new User("Dude").sayHi(); // Hello Dude!
لطفا توجه کنید که فراخوانی متد super.say() از sayHiMixin (در خطی که با (*) برچسبگذاری شده) در پروتوتایپ mixin به دنبال متد میگردد نه کلاس.
اینجا شکل آن را داریم (قسمت راست را ببینید):
دلیلش این است که sayHi و sayBye از اول درون sayHiMixin ایجاد شدهاند. پس حتی با اینکه کپی شدند، ویژگی درونی [[HomeObject]] آنها به sayHiMixin رجوع میکند، همانطور که در تصویر بالا نشان داده شده است.
چون super درون [[HomeObject]].[[Prototype]] به دنبال متدهای والد میگردد، یعنی sayHiMixin.[[Prototype]] را جستوجو میکند.
EventMixin
حالا بیایید یک mixin برای دنیای واقعی بسازیم.
یک خاصیت مهم در تعداد زیادی از شیءهای مرورگر (برای مثال) این است که آنها میتوانند رویداد (event) ایجاد کنند. رویدادها راهی عالی برای «انتشار اطلاعات» به هر کسی که آن را بخواهد هستند. پس بیایید یک mixin بسازیم که به ما این امکان را میدهد تا به راحتی تابعهای مربوط به رویداد را به هر شیء/کلاسی اضافه کنیم.
- این mixin متد
.trigger(name, [...data])را برای «ایجاد یک رویداد» زمانی که اتفاقی برای آن میافتد فراهم میکند. آرگومانnameاسم رویداد است که بعد از آن آرگومانهای اضافی اختیاری شامل دادۀ رویداد میآید. - همچنین متد
.on(name, handler)را فراهم میکند که تابعhandlerرا به عنوان کنترلکننده به رویدادهایی با نام داده شده اضافه میکند. این تابع زمانی که رویدادی همراه باnameداده شده راه میافتد (trigger) اجرا میشود و آرگومانها را از فراخوانی.triggerدریافت میکند. - …و متد
.off(name, handler)را هم فراهم میکند که کنترلکنندهhandlerرا حذف میکند.
بعد از اضافه کردن mixin، یک شیء user خواهد توانست زمانی که بازدیدکننده وارد میشود (log in) یک رویداد "login" ایجاد کند. و شیء دیگر، مثلا calendar (تقویم) شاید بخواهد چنین رویدادهایی را کنترل کند تا تقویم را برای شخص وارد شده بارگیری کند.
یا یک menu (فهرست) میتواند زمانی که چیزی از فهرست انتخاب شود رویداد "select" (انتخاب) را ایجاد کند و شیءهای دیگر ممکن است کنترلکنندههایی را برای واکنش دادن به این رویداد داشته باشند. و مثالهایی دیگر.
اینجا کد آن را داریم: