프로그래밍-학습기록/Javascript
자바스크립트 클래스 메소드에서 이벤트리스너 콜백 함수를 작성할 때 주의해야 할 this 바인딩
leesche
2021. 3. 10. 23:59
자바스크립트 클래스 안에서 이벤트리스너를 등록하고, 그 콜백 함수 안에서 this를 사용하려면 주의해야 한다.
아래는 board 엘리먼트의 자식 엘리먼트를 모두 선택해 각각에 이벤트리스너를 등록하는 메서드를 구현한 것이다.
export default class BoardHandler{
#gameManager;
/*
...
*/
#CLASS_NAME_BOARD_CHILD= ".board__child";
$allChildren = document.querySelectorAll(this.#CLASS_NAME_BOARD_CHILD);
#initializeBoardElement() {
this.$allChildren.forEach(($child) => ($child.textContent = ""));
this.$allChildren.forEach(($child) =>
$child.addEventListener(
"click",
this.#handleClickBoardSquare
)
);
}
#handleClickBoardChild() {
console.log(this); // <td> ... HTML Element
this.mark() // Error
}
mark() {
/* ... */
}
/*
...
*/
}
클래스 안에서 메서드를 작성할 때 this는 자연스럽게 해당 클래스의 인스턴스를 가리킨다.
하지만 이벤트리스너의 콜백함수로 호출될 때, 콜백함수 안에서 this는 클래스의 인스턴스를 가리키는 것이 아니다.
이 this는 이벤트가 일어난 HTML 엘리먼트를 가리킨다.
따라서 나는 이 상황을 아래와 같이 해결했다. 이벤트리스너를 부착할 때 콜백함수에 bind
메서드로 콜백함수가 호출될 때 this로 사용할 객체로 this(BoardHandler의 인스턴스), 그 다음 인자로 $child 엘리먼트를 전달했다.
이렇게 하면 문제 없이 의도한 대로 잘 실행된다.
export default class BoardHandler{
#gameManager;
/*
...
*/
#CLASS_NAME_BOARD_CHILD= ".board__child";
$allChildren = document.querySelectorAll(this.#CLASS_NAME_BOARD_CHILD);
#initializeBoardElement() {
this.$allChildren.forEach(($child) => ($child.textContent = ""));
this.$allChildren.forEach(($child) =>
$child.addEventListener(
"click",
this.#handleClickBoardSquare.bind(this, $child)
)
);
}
#handleClickBoardChild($child) {
const index = $child.id;
const activePlayer = this.#gameManager.getActivePlayer();
const type = activePlayer.getType();
this.mark($child, index, type);
}
/*
...
*/
}
물론 다른 방법으로 고차함수를 활용할 수도 있다. 하지만 이번 상황에서는 해당 이벤트리스너의 콜백함수가 기억해야 할 객체가 매번 변동되지 않고 일정해서 그럴 필요가 없겠다고 판단했다.