ìë°ì¤í¬ë¦½í¸ë ë¨ì¼ììë§ì íì©íë ì¸ì´ì
ëë¤. ê°ì²´ì ë¨ íëì [[Prototype]]ë§ ìì ì ìê³ , í´ëì¤ë í´ëì¤ íëë§ ììë°ì ì ììµëë¤.
ê·¸ë°ë° ê°ë ì´ë° ì ì½ì´ íê³ì²ë¼ ëê»´ì§ ëê° ììµëë¤. ìì를 ë¤ì´ë´
ìë¤. í´ëì¤ StreetSweeper(ëìì 거리를 ì²ìíë ì°¨ë â ì®ê¸´ì´)ì í´ëì¤ Bicycleì´ ìëë°, ì´ ëì ìì´ StreetSweepingBicycle를 ë§ë¤ê³ ì¶ë¤ê³ í´ë´
ìë¤.
ëë í´ëì¤ Userì ì´ë²¤í¸ë¥¼ ìì±í´ì£¼ë ì½ëê° ë´ê¸´ í´ëì¤ EventEmitterê° ìëë°, EventEmitterì 기ë¥ì Userì ì¶ê°í´ ì¬ì©ìê° ì´ë²¤í¸ë¥¼ ë´ë¿ì ì ìê²(emit) í´ì£¼ê³ ì¶ë¤ê³ í´ë´
ìë¤.
ì´ë´ ë 믹ì¤ì¸ì´ë¼ ë¶ë¦¬ë ê°ë ì ì¬ì©íë©´ ëìì´ ë©ëë¤.
Wikipediaìì 믹ì¤ì¸(mixin)ì ë¤ë¥¸ í´ëì¤ë¥¼ ììë°ì íì ìì´ ì´ë¤ í´ëì¤ì 구íëì´ìë ë©ìë를 ë´ê³ ìë í´ëì¤ë¼ê³ ì ìí©ëë¤.
ë¤ì ë§í´ì 믹ì¤ì¸ì í¹ì íëì ì¤íí´ì£¼ë ë©ìë를 ì ê³µíëë° ë¨ë ì¼ë¡ ì°ì´ì§ ìê³ ë¤ë¥¸ í´ëì¤ì íëì ëí´ì£¼ë ì©ëë¡ ì¬ì©ë©ëë¤.
믹ì¤ì¸ ìì
ìë°ì¤í¬ë¦½í¸ìì 믹ì¤ì¸ì 구íí ì ìë ê°ì¥ ì¬ì´ ë°©ë²ì ì ì©í ë©ìë ì¬ë¬ ê°ê° ë´ê¸´ ê°ì²´ë¥¼ íë ë§ëë ê²ì ëë¤. ì´ë ê² íë©´ ë¤ìì ë©ìë를 ìíë í´ëì¤ì íë¡í íì ì ì½ê² ë³í©í ì ììµëë¤.
ìë ììì 믹ì¤ì¸ sayHiMixinì Userìê² 'ì¸ì´ ë¥ë ¥âì ë¶ì¬í´ì¤ëë¤.
// 믹ì¤ì¸
let sayHiMixin = {
sayHi() {
alert(`Hello ${this.name}`);
},
sayBye() {
alert(`Bye ${this.name}`);
}
};
// ì¬ì©ë²:
class User {
constructor(name) {
this.name = name;
}
}
// ë©ìë ë³µì¬
Object.assign(User.prototype, sayHiMixin);
// ì´ì Userê° ì¸ì¬ë¥¼ í ì ììµëë¤.
new User("Dude").sayHi(); // Hello Dude!
ìì ìì´ ë©ìëë§ ê°ë¨í ë³µì¬íìµëë¤. 믹ì¤ì¸ì íì©íë©´ Userê° ìë ììì²ë¼ ë¤ë¥¸ í´ëì¤ë¥¼ ììë°ë ëìì, 믹ì¤ì¸ì 구íë ì¶ê° ë©ìëë ì¬ì©í ì ììµëë¤.
class User extends Person {
// ...
}
Object.assign(User.prototype, sayHiMixin);
믹ì¤ì¸ ììì 믹ì¤ì¸ ììì ì¬ì©íë ê²ë ê°ë¥í©ëë¤.
ìë ìììì sayHiMixinì sayMixinì ììë°ìµëë¤.
let sayMixin = {
say(phrase) {
alert(phrase);
}
};
let sayHiMixin = {
__proto__: sayMixin, // (Object.create를 ì¬ì©í´ íë¡í íì
ì ì¤ì í ìë ììµëë¤.)
sayHi() {
// ë¶ëª¨ ë©ìë í¸ì¶
super.say(`Hello ${this.name}`); // (*)
},
sayBye() {
super.say(`Bye ${this.name}`); // (*)
}
};
class User {
constructor(name) {
this.name = name;
}
}
// ë©ìë ë³µì¬
Object.assign(User.prototype, sayHiMixin);
// ì´ì Userê° ì¸ì¬ë¥¼ í ì ììµëë¤.
new User("Dude").sayHi(); // Hello Dude!
sayHiMixinìì ë¶ëª¨ ë©ìë super.say()를 í¸ì¶íë©´((*)ë¡ íìí ì¤) í´ëì¤ê° ìë sayHiMixinì íë¡í íì
ìì ë©ìë를 ì°¾ëë¤ë ì ì 주목í´ì£¼ì기 ë°ëëë¤.
그림ì ì°ì¸¡ì ë´ ìë¤.
ì´ë sayHiì sayByeê° ìì±ë ê³³ì´ sayHiMixinì´ê¸° ë문ì
ëë¤. ë°ë¼ì ë©ìë를 ë³µì¬íëë¼ë, ì´ ë©ìëë¤ì ë´ë¶ íë¡í¼í°ì¸ [[HomeObject]]ë ì 그림ì²ë¼ sayHiMixinì 참조í©ëë¤.
ë©ìëì superê° [[HomeObject]].[[Prototype]]ë´ìì ë¶ëª¨ ë©ìë를 찾기 ë문ì, ë©ìëë User.[[Prototype]]ì´ ìë sayHiMixin.[[Prototype]]ì ê²ìí©ëë¤.
ì´ë²¤í¸ 믹ì¤ì¸
ì¤ì ë¡ ì¬ì©í ì ìë 믹ì¤ì¸ì ë§ë¤ì´ë´ ìë¤.
ìë¹ì ë¸ë¼ì°ì ê°ì²´ë ì´ë²¤í¸ë¥¼ ìì±ì´ë¼ë ì¤ìí 기ë¥ì ê°ì§ê³ ììµëë¤. ì´ë²¤í¸ë ì 보를 íìë¡ íë ê³³ì âì 보를 ë리 ì리ë(broadcast)â íë¥í ìë¨ì ëë¤. ìë ìììì í´ëì¤ë ê°ì²´ì ì´ë²¤í¸ ê´ë ¨ í¨ì를 ì½ê² ì¶ê°í ì ìëë¡ í´ì£¼ë 믹ì¤ì¸ì ë§ë¤ì´ ë³´ê² ìµëë¤.
- 믹ì¤ì¸ì ëê° ì¤ìí ì¼ì´ ë°ìíì ë âì´ë²¤í¸ë¥¼ ìì±íëâ ë©ìë,
.trigger(name, [...data])를 ì ê³µí©ëë¤. ì¸ìnameì ì´ë²¤í¸ ì´ë¦ì´ê³ , ë¤ë°ë¥´ë ì í ì¸ìë ì´ë²¤í¸ ë°ì´í° ì 보를 ë´ìµëë¤. - ë©ìë
.on(name, handler)ìnameì í´ë¹íë ì´ë²¤í¸ì 리ì¤ëë¡handlerí¨ì를 ì¶ê°í©ëë¤..on()ì ì´ë²¤í¸(name)ê° í¸ë¦¬ê±° ë ë í¸ì¶ëê³ ,.triggerí¸ì¶ìì ì¸ì를 ì»ìµëë¤. - ë©ìë
.off(name, handler)ëhandler리ì¤ë를 ì ê±°í©ëë¤.
믹ì¤ì¸ì ì¶ê°íë©´, ì¬ì©ìê° ë¡ê·¸ì¸í ë ê°ì²´ userê° "login"ì´ë¼ë ì´ë²¤í¸ë¥¼ ìì±í ì ìê² ë©ëë¤. ë ë¤ë¥¸ ê°ì²´ calendarë userê° ìì±í ì´ë²¤í¸ì¸ "login"ì ë£ê³ ì¬ì©ìì ë§ë ë¬ë ¥ì ë³´ì¬ì¤ ì ìê² ì£ .
ë©ë´ì í목ì ì ííì ë ê°ì²´ menuê° "select"ë¼ë ì´ë²¤í¸ë¥¼ ìì±íê³ , ë¤ë¥¸ ê°ì²´ë "select"ì ë°ìíë ì´ë²¤í¸ í¸ë¤ë¬ë¥¼ í ë¹í ìë ìì ê²ëë¤. ì´ë²¤í¸ 믹ì¤ì¸ì ì´ë° ì©ëë¡ íì© ê°ë¥í©ëë¤.
ì´ë²¤í¸ 믹ì¤ì¸ì 구íí´ë´ ìë¤.
let eventMixin = {
/**
* ì´ë²¤í¸ 구ë
* ì¬ì©í¨í´: 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);
},
/**
* 구ë
ì·¨ì
* ì¬ì©í¨í´: menu.off('select', handler)
*/
off(eventName, handler) {
let handlers = this._eventHandlers?.[eventName];
if (!handlers) return;
for (let i = 0; i < handlers.length; i++) {
if (handlers[i] === handler) {
handlers.splice(i--, 1);
}
}
},
/**
* 주ì´ì§ ì´ë¦ê³¼ ë°ì´í°ë¥¼ 기ë°ì¼ë¡ ì´ë²¤í¸ ìì±
* ì¬ì©í¨í´: this.trigger('select', data1, data2);
*/
trigger(eventName, ...args) {
if (!this._eventHandlers?.[eventName]) {
return; // no handlers for that event name
}
// í¸ë¤ë¬ í¸ì¶
this._eventHandlers[eventName].forEach(handler => handler.apply(this, args));
}
};
.on(eventName, handler)âeventNameì í´ë¹íë ì´ë²¤í¸ê° ë°ìíë©´ ì¤íìí¬ í¨ìhandler를 í ë¹í©ëë¤. í ì´ë²¤í¸ì ëìíë í¸ë¤ë¬ê° ì¬ë¬ ê° ìì ë, íë¡í¼í°_eventHandlersë í¸ë¤ë¬ê° ë´ê¸´ ë°°ì´ì ì ì¥í©ëë¤. ì¬ê¸°ì í¸ë¤ë¬ê° ë°°ì´ì ì¶ê°ë§ ë©ëë¤..off(eventName, handler)â í¸ë¤ë¬ 리ì¤í¸ììhandler를 ì ê±°í©ëë¤..trigger(eventName, ...args)â ì´ë²¤í¸ë¥¼ ìì±í©ëë¤._eventHandlers[eventName]ì ìë 모ë í¸ë¤ë¬ê°...argsì í¨ê» í¸ì¶ë©ëë¤.
ì¬ì©ë²:
// í´ëì¤ ìì±
class Menu {
choose(value) {
this.trigger("select", value);
}
}
// ì´ë²¤í¸ ê´ë ¨ ë©ìëê° êµ¬íë 믹ì¤ì¸ ì¶ê°
Object.assign(Menu.prototype, eventMixin);
let menu = new Menu();
// ë©ë´ í목ì ì íí ë í¸ì¶ë í¸ë¤ë¬ ì¶ê°
menu.on("select", value => alert(`ì íë ê°: ${value}`));
// ì´ë²¤í¸ê° í¸ë¦¬ê±° ëë©´ í¸ë¤ë¬ê° ì¤íëì´ ì¼ë¿ì°½ì´ ë¸
// ì¼ë¿ì°½ ë©ìì§: Value selected: 123
menu.choose("123");
ì´ì menu.on(...)ì ì¬ì©í´ ë©ë´ ì íì´ë¼ë ì 보를 ë¤ì ì ìê² ëìê³ , ì´ì ë°ìíë ì½ë를 ì¶ê°í ì ìê² ëììµëë¤.
ê·¸ë¦¬ê³ ë¯¹ì¤ì¸ eventMixinì ì¬ì©íë©´ ì´ë° ëìì ìì ì²´ì´ëì ë¼ì´ë¤ì§ ìê³ ë ìíë í´ëì¤ì 모ëì ì¶ê°í ì ììµëë¤.
ìì½
믹ì¤ì¸ ì ê°ì²´ ì§í¥ ì¸ì´ìì ë²ì©ì ì¼ë¡ ì°ì´ë ì©ì´ë¡, ë¤ë¥¸ í´ëì¤ë¤ì ë©ìë ì¡°í©ì í¬í¨íë í´ëì¤ë¥¼ ì미í©ëë¤.
ëªëª ì¸ì´ë ë¤ì¤ììì íì©í©ëë¤. ìë°ì¤í¬ë¦½í¸ë ë¤ì¤ììì ì§ìíì§ ìëë°, 믹ì¤ì¸ì ì¬ì©íë©´ ë©ìë를 ë³µì¬í´ íë¡í íì ì 구íí ì ììµëë¤.
ì´ë²¤í¸ 믹ì¤ì¸ ìììì 본 ê²ì²ë¼, 믹ì¤ì¸ì ì´ë²¤í¸ í¸ë¤ë§ ë±ì íëì ì¶ê°íì¬ í´ëì¤ë¥¼ íì¥íë ì©ëë¡ ì¬ì©í ì ììµëë¤.
mixinì´ ì¤ìë¡ ê¸°ì¡´ í´ëì¤ ë©ìë를 ë®ì´ì°ë©´ ì¶©ëì´ ë°ìí ì ììµëë¤. ë°ë¼ì mixinì ë§ë¤ ë ì¶©ëì´ ë°ìíì§ ìëë¡ ë©ìë ì´ë¦ì ì ì¤íê² ì íì ì¼ í©ëë¤.

ëê¸
<code>í그를, ì¬ë¬ ì¤ë¡ 구ì±ë ì½ë를 ì½ì íê³ ì¶ë¤ë©´<pre>í그를 ì´ì©íì¸ì. 10ì¤ ì´ìì ì½ëë plnkr, JSBin, codepen ë±ì ìëë°ì¤ë¥¼ ì¬ì©íì¸ì.