[F-Lab 모각코 챌린지 5일차] 콜백 함수

Nami·2023년 6월 5일
0

66일 포스팅 챌린지

목록 보기
5/66
post-custom-banner

this 바인딩에 이어 그와 관련된 콜백 함수에 대해 공부해본다. 콜백 함수의 가장 마지막 섹션에 콜백 지옥과 비동기 제어라는 부분이 있는데, 이는 다음 회차에서 다루려 한다.


콜백 함수

콜백함수 callback function 란?

함수의 매개변수를 통해 다른 함수의 내부로 전달되는 함수.

  • 다른 코드(함수 또는 메서드)에게 인자로 넘겨줌으로써 그 제어권도 함께 위임한 함수.

  • 콜백함수를 위임받은 코드는 자체적인 내부 로직에 의해 이 콜백 함수를 적절한 시점에 실행할 것임. 매개변수를 통해 함수의 외부에서 콜백 함수를 전달받은 함수를 고차 함수 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을 호출할 때 두 개의 매개변수를 전달했다.

  1. 익명 함수
  2. 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]

Arrayprototype에 담긴 map 메서드의 구조

Array.prototype.map(callback[, thisArg])
callback: function(currentValue, index, array)
  • map 메서드는 첫 번째 인자로 callback 함수를 받고, 생략 가능한 두 번째 인자로 콜백 함수 내부에서 this로 인식할 대상을 특정할 수 있다.

  • thisArg를 생략할 경우 일반적인 함수와 마찬가지로 전역 객체가 this 바인딩 됨.

  • map 메서드는 메서드의 대상이 되는 배열의 모든 요소들을 처음부터 끝까지 하나씩 꺼내어 콜백 함수를 반복 호출하고, 콜백 함수의 실행 결과들을 모아 새로운 배열을 만듭니다.

    1. 첫 번째 인자 : 배열의 요소 중 현재값
    2. 두 번째 인자 : 현재값의 인덱스
    3. 세 번째 인자 : 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);

정리

  • 콜백 함수는 다른 코드에 인자로 넘겨줌으로써 그 제어권도 함께 위임한 함수이다.

  • 제어권을 넘겨받은 코드는 다음과 같은 제어권을 가진다.

    1. 콜백 함수를 호출하는 시점을 스스로 판단해서 실행한다.
    2. 콜백 함수를 호출할 때 인자로 넘겨줄 값들 및 순서는 정해져있다. 이 순서를 따르지 않고 코드를 작성하면 엉뚱한 결과를 얻게 된다.
    3. 콜백 함수의 this가 무엇을 바라보도록 할지 정해져 있는 경우도 있다. 정하지 않은 경우에는 전역객체를 바라본다. 사용자 임의로 this 를 바꾸고 싶은 경우 bind 메서드를 활용하면 된다.
      4. 어떤 함수에 인자로 메서드를 전달하더라도 이는 결국 함수로서 실행된다.

참조 ✅

  • 📚 『코어 자바스크립트』
    • 4-1 ~ 4-4. 콜백 함수
  • 📚 『모던 자바스크립트 Deep Dive』
    • 12.7.4 콜백 함수
post-custom-banner

0개의 댓글