콜백함수
다른 코드의 인자로 넘겨주는 함수. '넘겨준다'는 말은 콜백 함수를 넘겨받는 코드도 있다는 얘기이다. (예를들어 forEhch
,setTimeout
등) 콜백함수에서 넘겨받은 코드들은 콜백함수를 필요에 따라서 적절한 시점에 실행하게 된다. (제어권 = 넘겨 받은 코드)
var count = 0;
// timer : 콜백 내부에서 사용할 수 있는 '어떤 게 돌고있는지'
// 알려주는 id값
var timer = setInterval(function() {
console.log(count);
if(++count > 4) clearInterval(timer);
}, 300);
var count = 0;
var cbFunc = function () {
console.log(count);
if (++count > 4) clearInterval(timer);
};
var timer = setInterval(cbFunc, 300);
// 실행 결과
// 0 (0.3sec)
// 1 (0.6sec)
// 2 (0.9sec)
// 3 (1.2sec)
// 4 (1.5sec)
콜백 함수를 넘겨받은 setIntervals
가 언제 콜백함수를 호출할지 제어권을 갖고 있다.
0.3초(300)라는 적절한 시점을 함수에 적어놓은대로 실행을 한다.
==> 원래 cbFunc()을 수행한다며 호출주체와 제어권은 사용자가 되지만,
setInterval(cbFunc,300);으로 넘겨주게되면 호출주체는 모두 setIntervals
가 된다.
map함수는 각 배열 요소를 반환하여 새로운 배열을 반환한다. 기존 배열을 변경하지않고 새로운 배열을 생성하는데,
// map 함수에 의해 새로운 배열을 생성해서 newArr에 담고 있네요!
var 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 ]
여기에서 만약에 function (currentValue, index)
이 안에 변수 순서를 바꾼다면 자동으로 인식이 될 지 확인 해 봤다.
// map 함수에 의해 새로운 배열을 생성해서 newArr에 담고 있네요!
var 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 ]
결과는 변수의 순서를 바꾼게 인식이 되지않아서 currentValue
를 index
로 인식하고 인덱스에 +5한 결과가 반환 되었다.
==> 이처럼 map 메서드를 호출해서 원하는 배열을 얻고자 한다면 규칙대로 작성해야한다.
콜백 함수를 넘겨받은 코드에게 제어권이 있기때문에 인자의 순서까지도 넘겨받은 코드에게 제어권이 있다.
this
콜백함수도 함수이기때문에 기본적으로 this는 전역객체를 참조한다. 하지만 제어권을 넘겨받은 코드에서 콜백함수에 별도로 this가 될 대상을 지정한 경우에는 그 대상을 참조한다.
이걸 내부적으로 어떻게 가능하게 하는지, map함수를 구현해 본 코드가 있다.
// Array.prototype.map을 직접 구현해봤어요!
Array.prototype.mapaaa = function (callback, thisArg) {
var mappedArr = [];
for (var i = 0; i < this.length; i++) {
// call의 첫 번째 인자는 thisArg가 존재하는 경우는 그 객체, 없으면 전역객체
// call의 두 번째 인자는 this가 배열일 것(호출의 주체가 배열)이므로,
// i번째 요소를 넣어서 인자로 전달
var mappedValue = callback.call(thisArg || global, this[i]);
mappedArr[i] = mappedValue;
}
return mappedArr;
};
const a = [1, 2, 3].mapaaa((item) => {
return item * 2;
});
console.log(a);
이렇게 하니까 바로 제어권을 넘겨받은 코드에서 call/apply 메서드의 첫번째 인자에서 콜백 함수 내부에 사용될 this를 명시적으로 binding하기 때문에 this에 값이 담겼다.
그렇다면 이 코드를 이해 할 수 있다.
```javascript
//이젠 이 코드를 좀 더 잘 이해할 수 있어요!!
// setTimeout은 내부에서 콜백 함수를 호출할 때, call 메서드의 첫 번째 인자에
// 전역객체를 넘겨요
// 따라서 콜백 함수 내부에서의 this가 전역객체를 가리켜요
setTimeout(function() { console.log(this); }, 300); // Window { ... }
// forEach도 마찬가지로, 콜백 뒷 부분에 this를 명시해주지 않으면 전역객체를 넘겨요!
// 만약 명시한다면 해당 객체를 넘기긴 해요!
[1, 2, 3, 4, 5].forEach(function (x) {
console.log(this); // Window { ... }
});
//addEventListener는 내부에서 콜백 함수를 호출할 때, call 메서드의 첫 번째
//인자에 addEventListener메서드의 this를 그대로 넘겨주도록 정의돼 있어요(상속)
document.body.innerHTML += '<button id="a">클릭</button';
document.body.querySelector('#a').addEventListener('click', function(e) {
console.log(this, e);
});
콜백 함수로 어떤 객체의 메서드를 전달하더라도, 그 메서드는 메서드가 아닌 함수로 호출한다.
var obj = {
vals: [1, 2, 3],
logValues: function(v, i) {
console.log(this, v, i);
}
};
//method로써 호출
obj.logValues(1, 2);
//callback => obj를 this로 하는 메서드를 그대로 전달한게 아니에요
//단지, obj.logValues가 가리키는 함수만 전달한거에요(obj 객체와는 연관이 없습니다)
[4, 5, 6].forEach(obj.logValues);