JavaScript sadece bir objeden kalıtım yapmaya izin verir. Bir obje için sadece bir tane [[Prototype]] olabilir. Ayrıca bir sınıf sadece bir sınıfı geniÅletebilir.
Bu bizi sınırlandırabilir. ÃrneÄin, StreetSweeper ve Bycicle adında iki tane sınıfınız var ve bunlardan StreetSweepingBycicle adında bir sınıf yaratmak istiyorsunuz.
Veya programlama hakkında konuÅacak olursak, Rendereradında Åablonu uygulayan ve EventEmitter adında olayları iÅleyen bir sınıfımız olsun, ve bu fonksiyonaliteyi birlikte Page adında bir sınıfta kullanmak istiyoruz. Böylece page hem Åabloları kullanabiliecek hemde hemde olayları yayacak(emit).
Burada bize mixin konsepti yardımcı olabilir.
Wikipediaâda Åu Åekilde tanımlanmıÅtır: mixin sınıfı diÄer sınıflar tarafından kullanılacak metodları olan ve bunun için bir üst sınıfa ihtiyaç duymayan yapıdır.
DiÄer bir deyiÅle mixin belirli davranıÅları uygulayan metodları saÄlar, fakat bunu tek baÅına kullanmayız, bunu diÄer sınıflara baÅka davranıÅlar eklemek için kullanırız.
Mixin örneÄi
JavaScript mixini yapmak için en kolay yol kullanıÅlı metodlarla donatılmıŠbir objedir. Böylece kolayca birleÅtirebilir ve herhangi bir sınıfın prototipine koyabiliriz.
ÃrneÄin aÅaÄoda sayHiMixin, User için âspeechâ metodunu ekler:
// mixin
let sayHiMixin = {
sayHi() {
alert("Hello " + this.name);
},
sayBye() {
alert("Bye " + this.name);
}
};
// usage:
class User {
constructor(name) {
this.name = name;
}
}
// copy the methods
Object.assign(User.prototype, sayHiMixin);
// Artık User sayHi metodunu çaÄırabilir
new User("Dude").sayHi(); // Hi Dude!
Burada gördüÄünüz gibi kalıtım yoktur. Yapılan sadece basit metod kopyalamadır. User diÄer sınıfları geniÅletebilir, hatta bu sınıflar da kendi içerilerinde mixinâlere sahip olabilirler. Ãrnek vermek gerekirse:
class User extends Person {
// ...
}
Object.assign(User.prototype, sayHiMixin);
Mixinler kendi içlerinde kalıtım benzeri yapılar oluÅturabilirler.
ÃrneÄin sayHiMixin, sayMixinâten kalıtılmıÅtır:
let sayMixin = {
say(phrase) {
alert(phrase);
}
};
let sayHiMixin = {
__proto__: sayMixin, // (veya Object.create ile de prototipi ayarlayabilirdik)
sayHi() {
// call parent method
super.say("Hello " + this.name);
},
sayBye() {
super.say("Bye " + this.name);
}
};
class User {
constructor(name) {
this.name = name;
}
}
// metodları kopyala
Object.assign(User.prototype, sayHiMixin);
// artık kullanıcı sayHi'yi çaÄırabilir.
new User("Dude").sayHi(); // Hello Dude!
Dikkat ederseniz sayHiMixin içinde `super.say() çaÄırıldıÄında o mixinâin prototipindeki metoduna bakar, sınıfın deÄil.
Ãünkü sayHiMixin metodları [[HomeObject]]'e ayarlanmıÅtır. Bundan dolayı super aslında User.__proto__ deÄil de sayHiMixin.__proto__ anlamına gelir.
EventMixin
Artık gerçek olaylar için mixin yapabiliriz.
ÃoÄu objenin en önemli özelliÄi olaylar(event) çalıÅabilmesidir.
Bir obje önemli bir olay olduÄunda âolayâ yaratacak metoda sahip olmalıdır. DiÄer objeler ise böyle bir olayı âdinlemeliâ'dir.
Bir olay isme sahip olmalıdır, bunun ile birlikte ek verileri de barındırabilir.
ÃrneÄin user objesi kullanıcı giriÅ yapacaÄı zaman "login" olayını oluÅturabilir. DiÄer bir calendar objesi ise bu olayı alıp giriÅ yapan kullanıcı için takvimi doldurabilir.
Veya bir menu "select" adında menüden seçim yapıldıÄında oluÅturulan bir olay yaratabilir, diÄer objeler bilgi alabilir ve bu olaya göre iÅlem yapabilir.
Olaylar âbilgileri paylaÅmakâ için bir yöntemdir. Bunlar her sınıfta kullanıÅlı olabilir, bir örnek yapalım:
let eventMixin = {
/**
* Olaya kayıt olma, kullanımı:
* menu.on('select', function(item) { ... }
*/
on(eventName, handler) {
if (!this._eventHandlers) this._eventHandlers = {};
if (!this._eventHandlers[eventName]) {
this._eventHandlers[eventName] = [];
}
this._eventHandlers[eventName].push(handler);
},
/**
* Olaydan kaydı silme, kullanımı:
* menu.off('select', handler)
*/
off(eventName, handler) {
let handlers = this._eventHandlers && this._eventHandlers[eventName];
if (!handlers) return;
for(let i = 0; i < handlers.length; i++) {
if (handlers[i] == handler) {
handlers.splice(i--, 1);
}
}
},
/**
* Olay yarat ve buna veri ekle
* this.trigger('select', data1, data2);
*/
trigger(eventName, ...args) {
if (!this._eventHandlers || !this._eventHandlers[eventName]) {
return; // Bu olayın ismi ile baÅka kotarıcı yok.
}
// kotarıcıyı çaÄır.
this._eventHandlers[eventName].forEach(handler => handler.apply(this, args));
}
};
Burada 3 tane metod var:
.on(eventName, handler)handler(kotarıcı)'da belirtilen isimle bir olay çalıÅırsa kotarıcıyı ata. Kotarıcılar_eventHandlersözelliÄinde saklanır..off(eventName, handler)â kotarıcı listesinden fonksiyon siler..trigger(eventName, ...args)â olay yaratır; tüm kotarıcılar çaÄırılır veargsbunlara argüman olarak iletilir.
Kullanım:
// Sınıf yap
class Menu {
choose(value) {
this.trigger("select", value);
}
}
// mixin ekle
Object.assign(Menu.prototype, eventMixin);
let menu = new Menu();
// seçildiÄinde kotarıcıyı çaÄır.
menu.on("select", value => alert("Value selected: " + value));
// olayı çalıÅtır -> Value selected: 123 gösterilir
menu.choose("123"); // value selected
Artık kullanıcının seçimine farklılık gösteren bir kodumuz var ise bunu menu.on(...) ile kullanabiliriz.
eventMix böyle bir davranıÅa istediÄimiz kadar sınıfa eklenebilir bunu yaparken de kalıtım zincirine dokunulmamıŠolur.
Ãzet
Mixin â geniÅ bir nesne tabanlı programlama deyimidir buna göre; bir sınıf diÄer sınıflar için metodlar içerebilir.
Python gibi bazı diller birden fazla kalıtım ile mixin yaratmaya izin verir. JavaScript bunu desteklemez, fakat mixinleri prototipe kopyalayarak uygulanmasına izin verir.
Ayrıca mixinleri kullanarak bir sınıfın davranıÅlarını geniÅletebiliriz, bunu yukarıdaki olay-kotarıcıâda görmekteyiz.
Mixinler gerçek sınıfın metodlarının üzerine yazılarak çatıÅmaya neden olabilir. Bundan dolayı genellikle mixinleri isimlendirirken dikkatli olmalı ve problemi en aza indirmelisiniz.

Yorumlar
<code>kullanınız, birkaç satır eklemek için ise<pre>kullanın. EÄer 10 satırdan fazla kod ekleyecekseniz plnkr kullanabilirsiniz)