[JavaScript] 호이스팅과 콜백함수
호이스팅(hoisting)
프로그램에서 변수나 함수를 호출하거나 접근하는 코드가 함수 선언 코드보다 위에 있음에도 불구하고, 마치 선언 코드가 위에 있는 것처럼 동작하는 자바스크립트만의 기능
- 이 코드에서 함수 선언문이 함수 호출보다 아래에 있지만 오류가 발생하지 않는다.
func();
function func() {
console.log("hello"); // hello
}
호이스팅이 발생하는 이유
- 자바스크립트는 코드를 실행하기 전에 준비 단계를 거치는데 준비 단계에서 중첩 함수가 아닌 함수들은 모두 찾아 미리 생성해 둔다.
- 자바스크립트 코드는 이런 준비 단계를 거친 다음에 실행되므로 함수 선언 코드를 호출보다 늦게 작성해도 자연스럽게 호출할 수 있다.
- 자바스크립트의 독특한 특징의 하나인 호이스팅은 코드 내에서 함수 선언의 위치를 강제하지 않기 때문에 더 유연한 프로그래밍을 작성하는 데 도움을 주기도 한다.
💡 함수 표현식으로 만든 함수는 함수 선언으로 만든 함수와는 달리 호이스팅되지 않는다.
funcA(); // 출력 : "hello"
funcB(); // 오류 : funcB는 정의되지 않았으며 함수가 아닙니다
// 함수 선언식
function funcA() {
console.log("func A");
}
// 함수 표현식
let funcB = function () {
console.log("func B");
};
- funcA는 함수 선언으로 만들었기 때문에 호이스팅의 대상입니다 → 따라서 함수 선언 전에도 호출 가능
- funcB에 저장한 함수는 함수 표현식으로 만들었으므로 선언이 아닌 ‘값’으로 취급 → 함수 호이스팅 불가능
콜백 함수 (Callback Function)
함수는 다른 함수의 인수(인수=값)로도 전달할 수 있는데, 이를 ‘콜백 함수(Callback Function)’라고 한다.
function parentFunc(callBack) { // 매개변수 callBack에는 함수 childFunc이 저장됩니다
console.log("parent");
callBack();
}
function childFunc() {
console.log("child");
}
parentFunc(childFunc); // ①
// 출력
// parent
// child
- 함수 선언으로 2개의 함수 parentFunc와 childFunc을 만들고 ①에서 함수 parentFunc을 호출 → 인수로 함수 childFunc을 전달한다.
- 따라서 함수 parentFunc의 매개변수 callback에는 함수childFunc이 저장된다
- 함수 parentFunc을 호출하면 먼저 parentFunc은 parent를 콘솔에 출력한다.
- 그런 다음 매개변수 callback에 저장된 함수를 호출한다.
- 매개변수 callback에는 ①에서 인수로 전달된 함수 childFunc이 저장되어 있다.
- 따라서 함수 callback 을 호출하면 함수 childFunc이 실행되어 ‘child’를 콘솔에 출력한다.
- 콜백 함수를 인수로 받는 함수를 고차 함수(Higher-Order Function, HOF)라고 한다.
1. 콜백 함수가 필요한 이유
function repeat(count) { // ①
for (let idx = 0; idx < count; idx++) {
console.log(idx + 1);
}
}
repeat(5); // 1 2 3 4 5
① 함수 repeat는 매개변수 count만큼 반복하면서 현재의 반복이 몇 번째인지 콘솔에 출력한다.
- 만약 함수 repeat와 동일한 구조로 반복하는 반복문이지만 다른 기능이 추가적으로 필요하다면 일반적으로 다음과 같이 새 함수를 만든다.
(함수 repeatDouble은 repeat처럼 전달된 숫자만큼 반복하는 작업은 동일하지만, 반복문에서 수행하는 명령이 조금 다름)
function repeat(count) {
for (let idx = 0; idx < count; idx++) {
console.log(idx + 1);
}
}
function repeatDouble(count) { // ①
for (let idx = 0; idx < count; idx++) {
console.log((idx + 1) * 2);
}
}
repeatDouble(5); // 2 4 6 8 10
💡 함수가 동일한 기능을 갖더라도 특정 부분이 달라 새 함수를 만들게 되면 중복코드가 발생 → 콜백 함수를 사용하여 해결 가능
function repeat(count, callBack) { // ③
for (let idx = 0; idx < count; idx++) { // ④
callBack(idx + 1);
}
}
function origin(count) { // ①
console.log(count);
}
repeat(5, origin); // ②
// 출력 : 1 2 3 4 5
① 매개변수를 콘솔에 출력하는 함수 origin을 만든다.
② 함수 repeat를 호출하고 인수로 5와 함수 origin을 전달한다.
③ 함수 repeat가 호출되면 매개변수 count에는 숫자 5를 저장하고, callback에는 함수 origin을 저장한다.
④ 0부터 4까지 총 5회 반복할 때마다 매개변수 callback에 저장한 함수 origin을 호출하고 idx + 1을 인수로 전달한다. 따라서 함수 origin은 함수 repeat의 반복문에서 총 5회 호출되면서 숫 자 1부터 5까지 콘솔에 출력
- 만일 함수 repeat에서 반복문의 동작을 변경하고 싶다면, 새 함수를 만들어 repeat 의 인수로 전달
function repeat(count, callBack) { // ③
for (let idx = 0; idx < count; idx++) {
callBack(idx + 1);
}
}
function origin(count) {
console.log(count);
}
function double(count) { // ①
console.log(count * 2);
}
repeat(5, double);
// 출력 : 2 4 6 8 10
① 매개변수에 2를 곱해 콘솔에 출력하는 함수 double을 만듭니다.
② 함수 repeat의 인수로 함수 double을 전달합니다. 이제 함수 repeat의 매개변수 callback에는 함수 double이 저장됩니다.
③ 함수 repeat의 반복문에서 반복할 때마다 매개변수 callback에 저장된 함수 double을 호출하고 인수로 idx + 1을 전달합니다. 따라서 2 4 6 8 10이 콘솔에 출력됩니다.
- 만일 함수 repeat 내에서 또 다른 일을 하고 싶다면, 새 함수를 만들고 인수로 전달해 콜백 함수를 교체하면 됩니다. 콜백 함수를 이용하면 상황에 맞게 하나의 함수가 여러 동작을 수행하도록 만들 수 있습니다.
2. 함수 표현식을 이용한 콜백 함수
- 콜백 함수는 함수 표현식으로도 만들 수 있다.
function repeat(count, callBack) {
for (let idx = 0; idx < count; idx++) {
callBack(idx + 1);
}
}
const double = function (count) { // ①
console.log(count * 2);
};
repeat(5, double); // ②
// 출력 : 2 4 6 8 10
① 받은 인수에 2를 곱해 콘솔에 출력하는 익명 함수를 만들어 double에 저장
② 변수 double에 저장된 함수를 repeat의 인수로 전달해 콜백 함수로 사용
- 변수 double에 저장한 익명 함수를 다시 사용할 필요가 없는 상황이라면, 다음과 같이 익명 함수를 직접 인수 형태로 전달 → 코드 단축 가능
function repeat(count, callBack) {
for (let idx = 0; idx < count; idx++) {
callBack(idx + 1);
}
}
repeat(5, function (count) { // ①
console.log(count * 2);
});
// 출력 : 2 4 6 8 10
① 함수 repeat를 호출하며 첫 번째 인수로는 숫자 5, 두 번째 인수로는 콜백 함수로 활용할 익명 함수를 직접 생성해 전달한다. 그럼 익명 함수는 함수 repeat의 매개변수 callback에 저장되어 repeat 내에서 호출할 수 있게 된다