[JavaScript][TIL]콜백 함수란?, 제어권

Trippy·2023년 10월 24일
1

JavaScript

목록 보기
13/28
post-thumbnail

[1] 콜백함수

우리는 이미 콜백 함수를 사용해 왔다, 예를 들면 setTimeout, 배열에 대한 forEach등...

// setTimeout
setTimeout(function() {
  console.log("Hello, world!");
}, 1000);

// forEach
const numbers = [1, 2, 3, 4, 5];

numbers.forEach(function(number) {
  console.log(number);
});

(1) 콜백함수란?

  1. 다른 코드의 인자로 넘겨주는 함수이다, 인자로 넘겨준다는 얘기는 콜백함수를 넘겨받는 코드가 있다는 얘기다. forEach, setTimeout등이 있다

  2. 콜백 함수를 넘겨받은 위와 같은 코드 forEach, setTimeout등은 이 콜백 함수를 필요에 따라 적절한 시점에 실행하게 된다(제어권이 그들에게 있다)

  3. 이하 그림 설명..

  4. callback = call(부르다) + back(되돌아오다) = 되돌아와서 호출해줘!

    다시 말하자면, 제어권을 넘겨주는 대신 너가 알고 있는 그 로직으로 처리해줘!

  5. 즉 콜백 함수는 다른 코드(함수 또는 메서드)에게 인자로 넘겨줌으로써 그 제어권도 함께 위임한 함수. 콜백 함수를 위임받은 코드는 자체적으로 내부 로직에 의해 이 콜백 함수를 적절한 시점에 실행 <= 이 적절한 시점 역시 제어권이 있는 위임받은 코드가 가지고 있다.

(2) 제어권

1. 호출 시점

콜백 함수의 제어권을 넘겨받은 코드는 콜백 함수 호출 시점에 대한 제어권을 가진다.

아래 예시에서는 콜백 함수의 제어권을 받은 코드(=setinterval)가 언제 콜백함수를 호출할지에 대한 제어권을 가지게 된다, 0.3초라는 적절한 시점을 본인의 함수에 적어놓는대로 실행하는 것이다.

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)

그 모습을 아래 표 처럼 정리해본다.

2. 인자

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 ]

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 ]

컴퓨터는 사람이 아니기 때문에, index - currentValue의 의미를 사람처럼 이해할 수 없다. 따라서 의도치 않은 값이 나온다.

이처럼 map메서드를 호출해서 원하는 배열을 얻고자 한다면 정의된 규칙대로 작성해야 한다.
이 모든것은 전적으로 map메서드, 즉 콜백 함수를 넘겨받은 코드에게 그 제어권이 있다. 인자까지도 제어권이 그것에게 있다.

제어권이 넘어갈 map함수의 규칙에 맞게 '나는' 호출해야 한다.

3. this

콜백 함수도 함수이기 때문에 기본적으로는 this가 전역객체를 참조한다.

제어권을 넘겨받을 코드에서 콜백함수에 별도로 this가 될 대상을 지정한 경우에는 그 대상을 참조한다.

핵심은 call , appply에 있다.

// 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에 다른 값이 담길 수 있는것이다.

제어권을 넘겨받을 코드에서 콜백 함수에 별도로 this가 될 대상을 지정 한 경우에는 그 대상을 참조한다

//이젠 이 코드를 좀 더 잘 이해할 수 있어요!!

// 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);
});
profile
감금 당하고 개발만 하고 싶어요

0개의 댓글