자바스크립트는 다중상속을 지원하지 않지만 함수의 유연성을 사용하면 다중상속과 유사한 기능을 이용할 수 있다.
객체 믹스인
let sayHiMixin = {
sayHi() {
console.log(`Hello ${this.name}`);
},
sayBye() {
console.log(`Bye ${this.name}`);
}
};
메소드를 담은 객체가 있다.
class User {
constructor(name) {
this.name = name;
}
}
User클래스로 생성되는 객체가 sayHiMixin객체의 메소드들을 사용하게하려면 어떤 방법이 좋을까?
User클래스가 sayHiMixin을 상속하는것은 좋아 보이지 않는다.
User.prototype객체에 sayHiMixin객체를 추가하면 된다.
Object.assign(User.prototype, sayHiMixin);
new User("Dude").sayHi(); // Hello Dude!
User객체가 상속받은것처럼 사용할 수 있다.
이벤트믹스인
이벤트 추가, 삭제, 트리거하는 메소드를 가진 객체를 만든후 이벤트가 필요한 객체.prototype에 이벤트객체를 추가하면 상속받은것처럼 사용할 수 있다.
let eventMixin = {
_eventHandlers:new Object(null),
on(eventName,handler){
// eventName 이벤트가 등록되어있지 않다면 등록한다.
this._eventHandlers[eventName]??=[];
// 이벤트를 추가한다.
this._eventHandlers[eventName].push(handler);
}
off(eventName,handler){
// 이벤트목록에 eventName가 없으면 리턴
if(!this._eventHandlers[eventName]){
return;
}
let evArr = this._eventHandlers[eventName];
evArr.forEach((ev,index) => {
if(ev === handler){
evArr.splice(index--,1);
}
});
}
trigger(eventName, ...args){
//이벤트목록에 없다면 리턴
if(!this._eventHandlers[eventName]){
return;
}
this._eventHandlers[eventName].forEach(handler => {
handler.apply(this,args);
});
}
};
class Menu {
choose(value) {
this.trigger("select", value);
}
}
Object.assign(Menu.prototype, eventMixin);
let menu = new Menu();
menu.on("select", value => console.log(`선택된 값: ${value}`));
menu.choose("123");
menu.off("select", value => console.log(`선택된 값: ${value}`));
리터럴 핸들러가 추가되면 삭제할 방법이 없고 메모리해제할 수 없게된다.
let eventMixin = {
_eventHandlers:new Object(null),
on(eventName,handler){
// eventName 이벤트가 등록되어있지 않다면 등록한다.
this._eventHandlers[eventName]??=[];
// 이벤트를 추가한다.
this._eventHandlers[eventName].push(handler);
}
off(eventName,handler){
// 이벤트목록에 eventName가 없으면 리턴
if(!this._eventHandlers[eventName]){
return;
}
let evArr = this._eventHandlers[eventName];
evArr.forEach((ev,index) => {
if(ev === handler){
evArr.splice(index--,1);
}
});
// 원소가 0이면 배열삭제
if(!evArr.length){
return delete evArr;
}
}
trigger(eventName, ...args){
//이벤트목록에 없다면 리턴
if(!this._eventHandlers[eventName]){
return;
}
this._eventHandlers[eventName].forEach(handler => {
handler.apply(this,args);
});
}
};
class Menu {
choose(value) {
this.trigger("select", value);
}
}
Object.assign(Menu.prototype, eventMixin);
let menu = new Menu();
let fn = value => console.log(`선택된 값: ${value}`);
menu.on("select",fn );
menu.choose("123");
menu.off("select", fn);
핸들러를 변수에 저장하여 등록하고 삭제하면 삭제가 가능해진다.