2025. 5. 10. 14:53ㆍCoding Study/Javascript
콜백함수란
콜백 함수는 다른 코드의 인자로 넘겨주는 함수
다른코드에게 인자로 넘겨줌으로써 제어권도 함께 위임한다.
제어권
1) 호출 시점
콜백 함수의 제어권을 넘겨받은 코드는 콜백 함수 호출 시점에 대한 제어권을 가진다.
let count = 0;
let cbFunc = function () {
console.log(count);
if (++count > 4) clearInterval(timer);
};
let timer = setInterval(cbFunc, 300);
// -- 실행 결과 --
// 0 (0.3초)
// 1 (0.6초)
// 2 (0.9초)
// 3 (1.2초)
// 4 (1.5초)
| 실행 | count값(출력) | ++count 결과 | 비교 count > 4 | 멈춤 여부 |
| 1회 | 0 | 1 | false | 계속 실행 |
| 2회 | 1 | 2 | false | 계속 실행 |
| 3회 | 2 | 3 | false | 계속 실행 |
| 4회 | 3 | 4 | false | 계속 실행 |
| 5회 | 4 | 5 | true | ✅ 멈춤! |
setInterval 이라고 하는 '다른 코드'에 첫 번째 인자로서 cbFunc 함수를 넘겨주어서 setInterval 이 스스로 판단에 따라 적절한 시점인
0.3 초마다 익명 함수를 실행 했다.
< 참고 : 후위 전위 증가 연산자 >
let a = 5;
let b = a++; // 후위 증가
console.log(a); // 6
console.log(b); // 5 (a를 증가시키기 전의 값이 b에 들어감)
let x = 5;
let y = ++x; // 전위 증가
console.log(x); // 6
console.log(y); // 6 (x를 증가시킨 후의 값이 y에 들어감)
- a++: 변수 a의 현재 값을 먼저 b에 대입한 뒤, 그 후 a를 1 증가시킴.
- ++x: 먼저 x를 1 증가시키고, 증가된 값을 y에 대입.
2) 인자
콜백 함수의 제어권을 넘겨받은 코드는 콜백 함수를 호출 할 때 인자에 어떤 값들을 어떤 순서로 넘길 것인지에 대한 제어권을 가진다.
const newArr = [10, 20, 30].map(function(currentValue, index) {
console.log(currentValue, index);
return currentValue + 5;
});
console.log(newArr);
// -- 실행 결과 --
// 10 0
// 20 1
// 30 2
// [15, 25, 35]
<map 매서드의 구조>
첫번째 인자 : callback 함수
두번째 인자 : (생략가능) 콜백 함수 내부에서 this 로 인식할 대상 특정 생략 시 this 는 전역 객체
Array.prototype.map(callback[, thisArg])
callback : function(currentValue, index, array)
map 매서드의 callback 함수의 첫번째 인자에 index 두번째 인자에 currentValue 를 작성 하였다.
하지만 순서에 따라서 인자를 인식 하기 때문에 index 에 배열의 값이 들어가고, currentValue 에 배열의 index 값이 전달된다.
const newArr2 = [10, 20, 30].map(function(index, currentValue) {
console.log(index, currentValue);
return currentValue + 5;
});
console.log(newArr2);
// -- 실행 결과 --
// 10 0
// 20 1
// 30 2
// [5, 6, 7]
3) this
콜백 함수는 기본적으로 this 가 전역객체를 참조하지만, 제어권을 넘겨받은 코드에서 콜백 함수에 별도로 this 가 지정한 경우에는
그 대상을 참조 하게 된다.
Array.map(callback, thisArg)
callback (배열의 값이 순차적으로 들어감, 인덱스, Array)
const newArr = [10, 20, 30].map(
function (currentValue, index, arry) {
console.log(currentValue, index, arry, this); // 10 0 [ 10, 20, 30 ] [ 'this', 'arry' ]
return currentValue + 5; // 20 1 [ 10, 20, 30 ] [ 'this', 'arry' ]
}, // 30 2 [ 10, 20, 30 ] [ 'this', 'arry' ]
["this", "arry"]
);
console.log(newArr); // [ 15, 25, 35 ]
map 매서드에서 this 에 다른 값이 담기는 이유는 제어권을 넘겨받은 코드에서 call/apply 로 this 값을 명시적으로 바인딩 하기 때문
Array.prototype.map = function(callback, thisArg) {
const mappedArr = [];
for (let i = 0; i < this.length; i++) {
const mappedValue = callback.call(thisArg || window, this[i], i, this);
mappedArr[i] = mappedValue;
}
return mappedArr;
};
setTimeout 은 내부에서 콜백 함수를 호출할때 call 매서드의 첫 번째 인자에 전역객체를 넘기기 때문에 this 는 전역 객체이고,
forEach 에서는 별도의 인자로 this 를 작성 하지 않았기 때문에 this 가 전역객체를 가리킨다.
setTimeout(function() {
console.log(this);
}, 300); // (1) Window { ... }
[1, 2, 3, 4, 5].forEach(function(x) {
console.log(this); // (2) Window { ... }
});
addEventListener 의 경우에는 매서드 앞의 인자를 this 로 넘기기 때문에 button 엘리먼트가 this 가 된다.
document.body.innerHTML += '<button id="a">클릭</button>';
document.body.querySelector('#a').addEventListener(
'click',
function(e) {
console.log(this, e); // (3) <button id="a">클릭</button>
} // MouseEvent { isTrusted: true, ... }
);
콜백 함수는 함수다
매서드로서 호출이 되면 this 은 앞의 객체 obj 를 가리키지만,
매서드를 forEach 함수의 콜백 함수로 전달 하게 되면 this 는 전역 객체를 가리킨다.
앞서 실행 컨텍스트/ this 에서도 나왔 듯이 함수로 호출이 되면 this 는 전역객체를 가리킨다.
const obj = {
vals: [1, 2, 3],
logValues: function(v, i) {
console.log(this, v, i);
},
};
obj.logValues(1, 2); // { vals: [1, 2, 3], logValues: f } 1 2
[4, 5, 6].forEach(obj.logValues); // Window { ... } 4 0
// Window { ... } 5 1
// Window { ... } 6 2
콜백 함수 내부의 this 에 다른 값 바인딩 하기
전통적인 방식으로 this 를 바인딩 하는 방법
this 를 변수에 담아서 바로 전달한다.
const obj1 = {
name: "obj1",
func: function () {
let self = this; // this 를 변수에 담아서
return function () {
console.log(self.name); // 변수에 담은 this를 함수에 전달
};
},
};
const callback = obj1.func();
setTimeout(callback, 1000); // obj1
직접적으로 this 대신 자기 자신을 작성하는 방법도 있지만 다양한 상황에서 사용은 불가능 하다.
const obj1 = {
name: 'obj1',
func: function() {
console.log(obj1.name);
},
};
setTimeout(obj1.func, 1000);
<추천> ES5 에서 등장한 bind 메서드 활용한 this 바인딩 방법
const obj1 = {
name: 'obj1',
func: function() {
console.log(this.name);
},
};
setTimeout(obj1.func.bind(obj1), 1000); // 1초뒤 ob1 출력
const obj2 = { name: 'obj2' };
setTimeout(obj1.func.bind(obj2), 1500); // 1.5초뒤 ob2 출력
콜백 지옥과 비동기 제어
콜백 지옥의 예시
setTimeout(() => {
console.log("1번");
setTimeout(() => {
console.log("2번");
setTimeout(() => {
console.log("3번");
}, 2000);
}, 2000);
}, 2000);
// 2초뒤 "1번"
// 4초뒤 "2번"
// 6초뒤 "3번"
ES6 에서 등장한 Promise 적용
const timeoutPromise = (second) => // 시간을 인자로 받아서
new Promise((resolve, reject) => { // Promise 객체 생성
setTimeout(() => {
resolve("완료"); //then 이후 완료시 "완료"를 반환하는 함수 작성
}, second * 1000);
});
then 체인으로 순차적으로 실행이 가능하다.
timeoutPromise(2)
.then((res) => {
console.log(res); // 2초뒤 "완료"
return timeoutPromise(2); // 다음 then 의 res 로 전달
})
.then((res) => {
console.log(res); // 4초뒤 "완료"
return timeoutPromise(2); // 다음 then 의 res 로 전달
})
.then((res) => console.log(res)); // 6초뒤 "완료"
async / await 활용 (ES2017 부터 등장하였다.)
const promise1 = await timeoutPromise(2);
console.log(promise1); // 2초뒤 "완료"
const promise2 = await timeoutPromise(2);
console.log(promise2); // 4초뒤 "완료"
const promise3 = await timeoutPromise(2);
console.log(promise3); // 6초뒤 "완료"'Coding Study > Javascript' 카테고리의 다른 글
| 클래스 (2) | 2025.06.02 |
|---|---|
| prototype (0) | 2025.05.24 |
| 실행 컨택스트 (0) | 2025.04.23 |
| 자바스크립트 var, let, const 키워드 (0) | 2025.04.18 |
| this (0) | 2025.04.05 |