call, apply, bind 유용한 예제

1 minute read

call, apply 활용

call, apply 함수를 사용하면 유사객체도 객체의 메소드를 사용할 수 있다. querySelector 리턴값 NodeList는 대표적인 유사객체이다. 배열 내부에 길이 속성이 있어 length 메소드를 사용할 수 있지만, 배열의 고유 메소드인 map, forEach 등은 직접 사용할 수 없다. 하지만 아래 예제에서 볼 수 있듯이 apply, call, bind로 메소드를 빌려서 사용할 수 있다.

let allDivs = document.querySelectorAll("div") // nodeList는 유사배열이다.
console.log(allDivs.length) // 유사배열이지만 길이 속성이 있으므로 nodeList 길이를 출력한다
Arrray.prototype.map.call(allDivs, (el) => {
    return el.className;
});

bind 활용

1. 이벤트 핸들러(Event Handler)

bind는 this에 바인딩한 새로운 함수를 리턴한다. 즉시 실행하는 apply, call과의 차이점이다. 아래 예제에서 이벤트 핸들러에서 this는 HTML element인 <button>...</button>을 가리킨다.

let btn = document.querySelector("#btn");
btn.onclick = handleClick;
function handleClick() {
    console.log(this); // <button>...</button>
}

이는 this를 사전에 바인딩 해놓으면 해결할 수 있다.

btn.onclick = handleClick.bind(obj) // console.log(this)는 obj를 출력함

Hire Assessment 4번 문제를 풀 때, 이전에는 이벤트핸들러를 익명함수 내부에서 실행시켰다. 위 내용을 바탕으로 bind를 사용하여 수정하였다.

function test4(arr) {
  // TODO: 여기에 코드를 작성합니다.
  let ulElement = document.querySelector("#container");

  arr.forEach( (user) => {
    let liElement = document.createElement("li");
    let aElement = document.createElement("a");
    let divElement = document.createElement("div");

    aElement.classList.add("name");
    aElement.textContent = `${user.firstName} ${user.lastName}`;
    aElement.addEventListener("click", printRole.bind(null, user)); // 익명함수 대신 bind 함수를 사용함.

    divElement.classList.add("age");
    divElement.textContent = user.age;

    liElement.append(aElement, divElement);
    ulElement.append(liElement);
  })
}

2. setTimeout

setTimeout의 콜백함수는 다른 실행 컨텍스트에서 실행된다. this를 호출하면 전역객체 Windows를 출력한다.

class Rectangle {
  constructor(width, height) {
    this.area = width * height;
  }

  printArea() {
    console.log(this.area);
  }

  printAsync() {
    setTimeout(this.printArea, 1000)
  }
}

let board = new Rectangle(10, 20);
board.printAsync() // this가 windows 전역객체이므로 Error 발생

예전에는 익명함수를 사용했지만, 이제 bind를 사용할 수 있다. 콜백함수의 this를 인스턴스로 바인딩한다. 프로토타입 함수 내부이므로 this를 bind 함수의 인자로 전달하면 된다.

// 예시 1. bind
printAsync() {
  setTimeout(this.printArea.bind(this), 1000)
}

다른 방법으로는 화살표 함수가 있다. 화살표 함수의 this는 상위스코프의 this를 가리킨다는 것을 활용하자. 화살표 함수의 상위스코프는 인스턴스의 프로토타입 함수이기 때문에 this가 인스턴스로 바인딩된다.

// 예시 2. 화살표 함수
printAsync() {
  setTimeout( () => this.printArea, 1000)
}

Leave a comment