콜백 함수는 인수로 다른 함수에 전달되어, 외부 함수에서 호출되는 함수이다.
// 콜백 함수 예시
const btn = document.querySelector('button');
function handleLog () {
return console.log('함수다!');
}
btn.addEventListener('click', handleLog)
// button 클릭시 콘솔에 '함수다!'가 출력.
콜백 함수는 우리가 알게 모르게 많이 사용되고 있다. 코어 자바스크립트 4-3의 콜백 함수는 함수다에는 아래와 같은 문장이 나온다.
콜백 함수로 어떤 객체의 메서드를 전달하더라도 그 메서드는 메서드가 아니라 함수로서 호출된다.
어떤의미일까?
앞서 얘기했듯 콜백 함수는 다른 함수에 인자로 전달되어서 외부함수에서 호출 되는 함수라고 이야기 했었다. 외부함수에서 호출 될 때는 특정 이벤트 또는 조건에 따라 실행된다. 이때 콜백 함수를 객체의 메서드로 전달하면 해당 메서드를 함수로 전달하게 된다. 따라서 해당 메서드를 호출할 때는 해당 객체의 메서드가 아닌, 함수로서 실행된다.
아래는 메서드를 콜백 함수로 전달한 경우의 예시
const myObject = {
number: [1, 2, 3],
printNumber: function(n) {
console.log(this, n);
}
};
myObject.printNumber(10);
// 출력 결과
// {number: Array(3), printNumber: ƒ} 10
[10, 20, 30].forEach(myObject.printNumber);
// 출력 결과
// window{ ... } 10
// window{ ... } 20
// window{ ... } 30
[10, 20, 30].forEach(myObject.printNumber);
부분을 보면 myObject의 메서드를 콜백 함수로 전달하더라도 메서드가 아닌 함수로서 호출되는 것을 알 수 있다. 그런데 this의 출력 결과를 보면, 해당 함수 내부에서 this에 접근하면 객체 자체(myObject)가 아닌 전역 객체(window)를 참조하게 되어 window를 출력하는 것을 볼 수 있다. 점(.)으로 호출 되었기 때문에 메서드로서 호출되어 바로앞의 객체를 this가 가리킨다고 예상했지만 예상과는 다른 결과이다.
[10, 20, 30].forEach(myObject.printNumber)
에서 myObject.printNumber
는 객체 myObject 내부의 메서드이지만, forEach() 메서드의 실행에 따라 메서드의 컨텍스트가 바뀌기 때문에 window 객체에서 실행된다. forEach() 메서드를 실행하는 동안, 내부에서 myObject.printNumber
메서드를 콜백 함수로 전달하면, 자바스크립트 엔진은 forEach() 메서드의 두 번째 인수인 thisArg를 컨텍스트로 사용한다. 따라서 별도로 컨텍스트를 지정하지 않으면, 전역 객체인 window 객체가 컨텍스트로 사용된다.
이를 방지하기 위해서는 함수를 호출할 때 this를 함께 전달하는 방법이 있거나, 함수 내부에서 this를 bind를 이용하여 객체로 바인딩하는 방법이 있다.
다음과 같이 코드를 작성할 수 있다.
[10, 20, 30].forEach(myObject.printNumber.bind(myObject));
bind()
메서드를 사용하여 myObject.printNumber()
메서드의 컨텍스트를 항상 myObject로 설정하면, forEach() 메서드에서도 myObject 객체가 컨텍스트로 사용된다.
결국 어떤 함수의 인자에 객체의 메서드를 전달하더라도 이는 결국 메서드가 아닌 함수일 뿐이다. 이 차이를 이해하는 것이 중요하다.