다중상속 믹스인

lee jae hwan·2022년 7월 30일

javascript

목록 보기
69/107

자바스크립트는 다중상속을 지원하지 않지만 함수의 유연성을 사용하면 다중상속과 유사한 기능을 이용할 수 있다.

객체 믹스인

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);

핸들러를 변수에 저장하여 등록하고 삭제하면 삭제가 가능해진다.

0개의 댓글