다른 코드의 인자로 넘겨주는 함수
= 제어권을 넘겨줄테니 알고있는 로직으로 처리해줘
ex)forEach
,setTimeout
call (부르다) + back (되돌아오다) = 되돌아와서 호출해줘
예를들어,
함수/메서드에 인자로 넘겨줌으로써 제어권도 같이 넘겨주는 함수
☝️ 콜백 함수의 제어권을 넘겨받은 코드는 콜백 함수 호출 시점에 대한 제어권을 가짐
var count = 0;
var timer = setInterval(function() {
console.log(count);
if(++count > 4) // ++한 count의 값이 4보다 크면
clearInterval(timer); //timer를 멈춰줘
}, 300); //300: 0.3초 간격으로 이 로직을 반복
위에 처럼 콜백 함수의 제어권을 넘겨받은 코드 setInterval
이 언제 콜백함수를 호출할지에 대한 제어권을 가진다.
이 코드는 300, 즉 0.3초라는 시점에 실행하겠다라는 것.
var count = 0;
var cbFunc = function () {
console.log(count);
if (++count > 4) clearInterval(timer);
};
var timer = setInterval(cbFunc, 300);
// 호출시점에 대한 제어권은 setInterval이 가지고 있음
// 실행 결과
// 0 (0.3sec)
// 1 (0.6sec)
// 2 (0.9sec)
// 3 (1.2sec)
// 4 (1.5sec)
원래 cdFunc()
를 수행하면 호출주체
와 제어권
은 사용자가 됨
setInterval
로 넘겨주면 호출주체
와 제어권
모두 setInterval
이 됨
map
함수 = 각 배열 요소를 변환하여 새로운 배열을 반환 -> 기존 배열을 변경 X
// 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)
에서 인자의 순서를 바꾼다면,
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 ]
//index랑 currentvalue는 사람이 인식할 수 있는거 일뿐,
//컴퓨터는 index를 current로 current를 index로
//자리처럼 인식
출력값이 달라지고, 의도하지 않은 값이 나온것을 확인할 수 있다.
왜?
이렇게,
map에서는 인자의 순서까지도 map에게 제어권이 있다.
< this가 전역객체를 참조하지 않는 예외사항 >
제어권을 넘겨받을 코드에서 콜백 함수에 별도로 this가 될 대상을 지정한 경우에는 그 대상을 참조
setTimeout(function() { console.log(this); }, 300); // Window { ... }
[1, 2, 3, 4, 5].forEach(function (x) {
console.log(this); // Window { ... }
});
document.body.innerHTML += '<button id="a">클릭</button';
document.body.querySelector('#a').addEventListener('click', function(e) {
console.log(this, e);
});
setTimeout
은 내부에서 콜백 함수를 호출할때, call 메서드의 첫번째 인자에 전역객체를 넘김
-> 콜백함수 내부에서 this는 전역객체를 가리킴
forEach
도 콜백함수 뒤에 this를 명시하지 않으면 전역객체를 가리킴
addEventListener
은 콜백함수 호출시, call 메서드의 첫번째 인자에 addEventListener
메서드의 this를 그대로 넘겨줌
콜백 함수 내에서 this 키워드를 사용하면, 기대한 대로 작동하지 않을 수가 있다.
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를 명시적으로 바인딩 하기 때문에 this에 다른 값이 담길 수 있다
콜백함수는 어떤 객체의 메서드를 전달하더라도. 메서드가 아닌
함수
로 호출
var obj1 = {
name: 'obj1',
func: function() {
var self = this; //this 참조!!!
return function () {
console.log(self.name);
};
}
};
// 단순히 함수만 전달한 것이기 때문에, obj1 객체와는 상관이 없음
var callback = obj1.func();
//func대신에 function () {
//console.log(self.name);를 담은것
setTimeout(callback, 1000);
이 코드에서
var callback = obj1.func(); = var callback = obj1.function () {
console.log(self.name);
이렇게 되는것과 같다. 하지만, 여기선 제대로된 this를 사용하는 건 아니다!
밑에 코드들을 보자.
// (1)
var obj1 = {
name: 'obj1',
func: function() {
var self = this; //이 부분!
return function () {
console.log(self.name);
};
}
};
// ---------------------------------
var obj2 = {
name: 'obj2',
func: obj1.func
};
var callback2 = obj2.func();
setTimeout(callback2, 1500);
var obj3 = { name: 'obj3' };
var callback3 = obj1.func.call(obj3);
setTimeout(callback3, 2000); //2초
//(2)
var obj1 = {
name: 'obj1',
func: function() {
var self = this;
return function () {
console.log(self.name);
};
}
};
// ---------------------------------
var obj2 = {
name: 'obj2',
func: function() {
var self = this;
return function () {
console.log(self.name);
};
};
var callback2 = function () {
console.log("obj2");
setTimeout(callback2, 1500);
var obj3 = { name: 'obj3' };
var callback3 = obj1.func.call(obj3);
setTimeout(callback3, 2000); //2초
(1)번 코드와 (2)번 코드는 this를 우회적으로 활용한 코드이다.
이게 전통적인 코드이지만, 가장 좋은 방법은 bind 메서드를 사용하는 방법
이다.
var obj1 = {
name: 'obj1',
func: function () {
console.log(this.name);
}
};
//함수 자체를 obj1에 바인딩
//obj1.func를 실행할 때 무조건 this는 obj1로 고정
setTimeout(obj1.func.bind(obj1), 1000);
var obj2 = { name: 'obj2' };
//함수 자체를 obj2에 바인딩
//obj1.func를 실행할 때 무조건 this는 obj2로 고정
setTimeout(obj1.func.bind(obj2), 1500);