this 바인딩에 이어 그와 관련된 콜백 함수에 대해 공부해본다. 콜백 함수의 가장 마지막 섹션에 콜백 지옥과 비동기 제어라는 부분이 있는데, 이는 다음 회차에서 다루려 한다.
함수의 매개변수를 통해 다른 함수의 내부로 전달되는 함수.
다른 코드(함수 또는 메서드)에게 인자로 넘겨줌으로써 그 제어권도 함께 위임한 함수.
콜백함수를 위임받은 코드는 자체적인 내부 로직에 의해 이 콜백 함수를 적절한 시점에 실행할 것임. 매개변수를 통해 함수의 외부에서 콜백 함수를 전달받은 함수를 고차 함수 Higher-Order Function 라고 한다.
콜백 함수는 고차 함수에 의해 호출되며 이때 고차 함수는 필요에 따라 콜백 함수에 인수를 전달할 수 있다.
콜백 함수는 제어권과 관련이 깊다. callback은 '부르다', '호출(실행)하다'는 의미인 call과, '뒤돌아오다', '되돌다'는 의미인 back의 합성어로 '되돌아 호출해달라' 는 명령.
어떤 함수 X를 호출하면서 '특정 조건일 때 함수 Y를 실행해서 나에게 알려달라' 는 요청을 함께 보내는 것. 함수 X의 입장에서는 해당 조건이 갖춰졌는지 여부를 스스로 판단 하고 Y를 직접 호출함.
var count = 0;
var timer = setInterval(function () {
console.log(count);
if (++count > 4) clearinterval(timer);
}, 300);
setInterval
을 호출할 때 두 개의 매개변수를 전달했다.
300
이라는 숫자setInterval
의 구조var intervalID = scope.setInterval(func, delay[, param1, param2, ...]);
scope
에는 Window 객체 또는 Worker의 인스턴스가 들어올 수 있음.
setInterval
메서드 제공window
생략, 함수처럼 사용매개변수 func
, delay
값을 반드시 전달, 세 번째 매개변수는 선택적
func
는 함수이고, delay
는 밀리초(ms) 단위의 숫자이며, 나머지param1, param2, ...
는 func
함수를 실행할 때 매개변수로 전달할 인자.setInterval
을 실행하면 반복적으로 실행되는 내용 자체를 측정할 수 있는 고유한 ID 값이 반환됨.
clearInterval
)할 수 있게 하기 위해서 var count = 0;
var cbFunc = function () {
console.log(count);
if (++count > 4)clearInterval(timer);
};
var timer = setInterval(cbFunc, 300);
// -- 실행 결과 --
// 0 (0.3초)
// 1 (0.6초)
// 2 (0.9초)
// 3 (1.2초)
// 4 (1.5초)
이 코드를 실행하면 콘솔창에는 0.3초에 한 번씩 숫자가 0부터 1씩 증가하며 출력되다가 4가 출력된 이후 종료된다.
setInterval
이라고 하는 '다른 코드'에 첫 번째 인자로서 cbFunc
함수를 넘겨주자 제어권을 넘겨받은 setInterval
이 스스로의 판단에 따라 적절한 시점에 (0.3초 마다) 이 익명 함수를 실행했다.
콜백 함수의 제어권을 넘겨받은 코드는 콜백 함수 호출 시점에 대한 제어권을 가진다.
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]
Array
의 prototype
에 담긴 map
메서드의 구조
Array.prototype.map(callback[, thisArg])
callback: function(currentValue, index, array)
map
메서드는 첫 번째 인자로 callback 함수를 받고, 생략 가능한 두 번째 인자로 콜백 함수 내부에서 this
로 인식할 대상을 특정할 수 있다.
thisArg
를 생략할 경우 일반적인 함수와 마찬가지로 전역 객체가 this 바인딩 됨.
map
메서드는 메서드의 대상이 되는 배열의 모든 요소들을 처음부터 끝까지 하나씩 꺼내어 콜백 함수를 반복 호출하고, 콜백 함수의 실행 결과들을 모아 새로운 배열을 만듭니다.
map
메서드의 대상이 되는 배열 자체위 예시에서 currentValue
, index
의 순서를 반대로 한다면?
사람은 단어로 접근하기 때문에 순서를 바꾸더라도 각 단어의 의미가 바뀌지 않으니까 문제가 없을 것이라 생각하기 쉽다. 하지만 저 단어는 사용자가 명명한 것일 뿐이다.
컴퓨터는 첫 번째, 두 번째의 순서에 의해서만 각각을 구분하고 인식할 것이다.
우리가 어떤 값을 칭하건, 순회 중인 배열 중 현재 요소의 값을 배정하는 것.
map
메서드를 호출해서 원하는 배열을 얻으려면 map
메서드에 정의된 규칙에 따라 함수를 작성해야한다. map
메서드에 정의된 규칙에는 콜백 함수의 인자로 넘어올 값들 및 그 순서도 포함되어 있다.
콜백함수를 호출하는 주체는 사용자가 아닌 map
메서드.
콜백 함수를 호출할 때 인자에 어떤 값들을 어떤 순서로 넘길 것인지는 전적으로 map
메서드에 달린 것이다.
콜백 함수의 제어권을 넘겨받은 코드는 콜백 함수를 호출할 때 인자에 어떤 값들을 어떤 순서로 넘길 것인지에 대한 제어권을 가진다.
this
Array.prototype.map = function (callback, thisArg) {
var mappedArr = [];
for (var i = 0; i < this.length; i++) {
var mappedValue = callback.call(thisArg || window, this[i], i, this);
mappedArr[i] = mappedValue;
}
return mappedArr;
};
메서드 구현의 핵심은 call/apply
메서드에 있다.
this
에는 thisArg
값이 있을 경우에는 그 값을, 없을 경우에는 전역객체를 지정
첫 번째 인자 : 메서드의 this
가 배열을 가리킬 것이므로
배열의 i번째 요소 값을 호출
두 번째 인자 : i 값 호출
세 번째 인자 : 배열 자체를 지정해 호출
mappedValue
에 담겨 mappedArr
의 i번째 인자에 할당됨.this
에 다른 값이 담기는 이유제어권을 넘겨받을 코드에서 call/apply
메서드의 첫 번째 인자에 콜백 함수 내부에서의 this
가 될 대상을 명시적으로 바인딩하기 때문.
콜백 함수로 어떤 객체의 메서드를 전달하더라도 그 메서드는 메서드가 아닌 함수로서 호출된다.
var 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
// 메서드의 이름 앞에 점이 있으니 메서드로서 호출한 것.
// this는 obj를 가리키고, 인자로 넘어온 1, 2가 출력됨.
[4, 5, 6].forEach(obj.logValues); // Window { ... } 4 0
// Window { ... } 5 1
// Window { ... } 6 2
// 메서드를 forEach 함수의 콜백 함수로서 전달함
// obj.logValues가 가리키는 함수만 전달함
// forEach에 의해 콜백이 함수로서 호출, 별도로 this를 지정하는 인자를 지정하지 않음.
// 함수 내부에서의 this는 전역 객체를 바라보게 됨.
어떤 함수의 인자에 객체의 메서드를 전달하더라도 이는 결국 메서드가 아닌 함수일 뿐이다.
this
에 다른 값 바인딩하기bind
메서드 활용var obj1 = {
name: 'obj1',
func: function () {
console.log(this.name);
}
};
setTimeout(obj1.func.bind(obj1), 1000);
var obj2 = { name: 'obj2' };
setTimeout(obj1.func.bind(obj2), 1500);
콜백 함수는 다른 코드에 인자로 넘겨줌으로써 그 제어권도 함께 위임한 함수이다.
제어권을 넘겨받은 코드는 다음과 같은 제어권을 가진다.
this
가 무엇을 바라보도록 할지 정해져 있는 경우도 있다. 정하지 않은 경우에는 전역객체를 바라본다. 사용자 임의로 this
를 바꾸고 싶은 경우 bind
메서드를 활용하면 된다.