다른 코드의 '인자'
로 넘겨주는 함수는 => 콜백함수
콜백함수 때문에 생기는 여러가지 특징이 있고,
그 특징 때문에 생기는 바람직하지 않는 상황이 있음.
콜백지옥 === 말 그대로 지옥
그래서 이런 것들을 다루기 위해서는
반드시 동기 , 비동기의 개념을 학습해야 한다.
이 비동기적인 것 때문에 발생하는 문제들 때문에
우리는 비동기적인 것을 동기적인 것으로 보이게 만들어야 한다.
우리가 넘겨준 콜백함수를 받은 주체가 된다.
그 콜백함수를 넘겨받은 주체 forEach
나 setTimeou
은
필요에 따라 적절한 시점에 실행된다 => 제어권
이 그들에게 있다.
알림시계로 콜백함수 예를 들자면
우리가 알람을 울리지 않고 시계가 제어권이 있기 때문에 울려준다.
즉 콜백함수는? 되돌아와서 호출해줘! === call + back
다시 말하면, 제어권을 넘겨줄테니 너가 알고 있는 그 로직으로 처리해줘!
setTimeout
안에 1초후 라는 로직이 있는데
안에 콘솔이 있었지만 setTimeout
의 자체의 1초후를 알아서 처리해줌
forEach
도 반복하는 로직을 알아서 반복해서 처리한 것
즉 콜백 함수는 다른 코드 (함수 또는 메서드)
에게 인자로 넘겨줌으로써
그 제어권도 함께 위임한 함수. 자체적으로 내부 로직에 의해
이 콜백 함수를 적절한 시점에 실행한다.
var count = 0;
var timer = setInterval(function(){
console.log(count);
if (++count > 4) clearInterval(timer);
}, 300);
여기서 timer
에 담아서 사용하는 이유는
setInterval
에는 timer
라는 속성이 있는데
이 변수에 담기면서 그 속성도 옮겨 담김
그래서 clearInterval
을 timer
로 지정해서 사용할 수 있음
undefined
가 뜰 것.++count
가 찍히니까 5가 된순간 4보다 커지니까
clearInterval
을 사용하면서 이 로직을 빠져나오게 된 것이다.
이렇게 되면서 이 호출 시점에 대한 제어권을 가지게 된거다.
0.3초에 대한 setInterval
이 제어권을 가지고 있다는 것이다.
매게변수는 정확히
function(){
console.log(count);
if (++count > 4) clearInterval(timer);
}
이 부분 까지이기 때문에 넘겨준 함수가 저기까지니까
그럼 이 매게변수를 이제 변수에 담고,
만약 직접 호출하게 된다면?
그럼 1번만 실행되고 멈춰버리게 된다.
cbFunc();
호출 주체가 우리가 되기 때문에 이 호출 시점에 대한
제어권을 우리가 가지고 있는 것이여서,
var timer = setInterval(cbFunc, 300);
이렇게 매개변수로 넣어주면 이 콜백함수의 대한 제어권이 우리에게 없고
제어권이 setInterval 함수
에 있기 때문에 결과가 달라진다.
var newArr = [10, 20, 30].map(function(currentValue, index){
console.log(currentValue, index);
});
console.log(newArr);
currentValue
는 배열안의 인자 값
index
는 배열 번호이다.
마찬가지로 배열을 순회하는 콜백함수인 map
함수다.
그렇다면 아래 console.log(newArr)
은 무슨 값이 나올까?
undefiend
3개가 나오는데
return문
이 없으면 map함수
는 무엇인가를 할당하긴 해야된다 배열 크기 만큼
할게 없어서 undefined
라도 할당한 것.
var newArr = [10, 20, 30].map(function(currentValue, index){
console.log(currentValue, index);
return currentValue + 5;
});
이렇게 인자값을 모두 +5 해서 return
한다는 뜻이고
바깥의 콘솔로 확인하면 15 25 35
가 할당된 것을 확인할 수 있다.
var newArr = [10, 20, 30].map(function(index, currentValue){
console.log(index, currentValue);
return currentValue + 5;
});
이렇게 되면? 바깥 콘솔에서 5,6,7로 나오는데
현재 + 5가 index
로 인식한 쌔한 느낌이 든다.
// index : 사람이 이해할 수 있는 변수명일 뿐
// currentValue : 사람이 이해할 수 있는 변수명일 뿐
즉 컴퓨터가 인식할 수 있는건 매개변수가 어디에 있는지 몇번째에 있는지
자릿수로 인식할 수 있다.
지금처럼 순서가 바뀌면 index
자리를 currentValue
로 인식하고 ,
currentValue
를 index
로 인식한다.
그러니까 아래처럼 return
문을 보면
index + 5
로 컴퓨터는 읽은것이다.
컴퓨터는 사람이 아니라 이런 시멘틱한 요소들을 이해할 수 없습니다.
항상 기억해야 하는게 map
을 map
처럼 사용하기 위해서는
원하는 것을 얻고자 한다면 정해진 규칙을 지키면서 사용해야 한다.
(MDN의 사용방법대로 사용해야 함)
사람이 아무리 이 인자를 제어하려해도 할 수 없다.
제어가 가능한건 이 map
이라는 함수 만이
콜백함수에 대한 인자의 제어권을 가지고 있는 것이다.
함수() / Object.메서드()
this
콜백 함수도 함수이기 때문에 기본적으로 this가 전역객체를 참조한다
메서드는 호출주체가 명확하기에 Object
를 보지만
함수는 명확하지 않기에 전역객체를 참조한다.
하지만 예외사항이 있다.
제어권을 넘겨받을 코드에서 콜백 함수에 별도로 this가 될 대상을
지정한 경우에는 그 대상을 참조한다.
- 호출 시점에 대한 제어권
- 인재에 대한 제어권
[4, 5, 6].forEach(obj.logValues);
이렇게 사용해야하는데
간혹가다 [4, 5, 6].forEach(obj.logValues(1, 4));
이렇게 사용하는 경유가 있는데
매개변수까지 넣어줘야 하는 거 아니냐고 하는데,
이렇게 쓰게 되면 매게변수로 쓰는게 아니라 함수의 결과로 넣에 되는 것이다.
함수로 매개변수로 넣는다는건 -> 함수 그 자체로 넣어야 한다는 것
[4, 5, 6].forEach(obj.logValues);
이렇게 썼을 때 원하지
않는 결과가 총 3번 출력되었다.
obj.logValues
를 넣었기 때문에 매서드를 넣었다고 생각하지만
이는 매서드의 형태처럼 보이지만 무엇을 넣었냐면
function(v, i) {
console.log('>>> test start');
if (this === global) {
console.log('this가 global입니다. 원하지 않는 결과!');
} else {
console.log(this, v, i);
}
console.log('>>> test end');
}
결국 나는 위 코드에서 function
에서 시작된 이 자체의 함수를
넣어줬을 뿐이다. 이 매서드로서의 호출은
호출만 해줘야지 매개변수로 무언갈 넘기려고 하면 안된다.
이게 왜 콜백함수는 함수인지를 보여주는 것이다
함수 자체로 출력만 필요로 한다.
this
에 다른 값 바인딩closure
방식 (다음주차에 자세한 설명)
: 현재 함수가 끝남음에도 영향력을 끼친다.
var obj1 = {
name: 'obj1',
func: function() {
var self = this; //이 부분!
return function () {
console.log(self.name);
};
}
};
var callback = obj1.func();
setTimeout(callback, 1000);
여기서 어느 부분이 func
에 담겨있냐면
function () { console.log(self.name); }; => (팁으로 return 다음걸 보면 됨.)
이 부분이 지금 들어가 있는 것이다.
근데 이 방식의 단점은 this
를 사용하지 않는데 있다는 것이다.
depth
가 깊기도 하고 번거롭고 귀찮은 작업이다. (위의 방식)
그렇다면 this를 빼버리면 어떨까?
var obj1 = {
name: 'obj1',
func: function () {
console.log(obj1.name);
},
};
setTimeout(obj1.func, 1000);
첫번 째 보다 간결해보이긴 하지만
this
를 사용하지 않으면서 결과만을 위한 코딩이 되었다.
즉 이 코드는 짧아보여도 10점짜리 코드인것이다.
// bind
var obj1 = {
name: 'obj1',
func: function () {
console.log(this.name);
}
};
// var boundObj1 = obj1.func.bind(obj1);
// setTimeout(boundObj1, 1000);
var obj2 = {name: 'obj2'};
setTimeout(obj1.func.bind(obj2), 1500);
아래 boundObj1 obj1
의 func
에 bind
를 해서 obj1
을 묶어주고
obj1이 this
로써 묶이고 obj1 객체가 this
로 묶이고
변수 obj1
의 객체 내부 함수에있는 this는 obj1
이랑 묶이는 것이다.
아래의 obj2
의 경우 obj1.func.bind
까진 맞는데 obj2
라는 객체를 bind
했다.
obj1
가 아닌 obj2
가 실행된다. -> 새로운 객체를 this.bind
한 것.
즉 어떤 this든 bind
해서 사용할 수 있다는 것을 보여줬다.