
- 학습 내용
자바스크립트에는 특별한 대우를 받는 일급 객체들이 있다. 오늘 학습할 함수 역시 대표적인 일급 객체이다. 그들이 받는 대우는 이러하다. '변수에 할당할 수 있음', '다른 함수의 인자로 전달할 수 있음', '다른 함수의 결과로 리턴될 수 있음', 특히 이 중에서 다른 함수의 인자로 전달하거나 다른 함수의 결과로 리턴하는 함수를 '고차함수'(higher order function)이라고 한다.
추상화라는 개념이 있는데, 추상화란 복잡한 것을 압축해서 핵심만 추출한 상태로 만드는 것이다. 가장 대표적인 추상화 중 하나가 숫자이다. 하나라는 개념을 1로 추상화한 것이다. 우리가 사용하는 컴퓨터 역시 추상화의 집약체라고 할 수 있다. 이런 추상화를 프로그래밍적 관점에서는 함수로 할 수 있다. 어떤 복잡한 개념을 핵심만 추출해서 집약하는 것이다. 그런데 여기서 멈추지 않고 한 단계 더 끌어올려 함수를 함수화한 것 즉, 사고의 집약을 다시 추상화한 것이 바로 고차함수이다. 이것을 '사고의 추상화'라고 한다. 고차함수를 이해하는 것에 있어 이 개념이 중요하다.
- 고차 함수
고차함수는 크게 두 가지 경우로 나누는데 다른 함수의 인자로 전달하는 경우와 다른 함수의 결과로 리턴하는 경우이다. 먼저, 다른 함수의 인자로 전달하는 경우에 인자(argument)로 전달하는 함수를 콜백함수(callback function)라고 하는데, 콜백 함수를 전달받은 고차 함수는 함수 내부에서 이 콜백함수를 호출해서 사용할 수 있다. 물론 조건에 따라 콜백함수의 실행 여부를 결정할 수도 있다. 여러 번 반복할 수도 아예 실행하지 않을 수도 있는 것이다.
function double(num) {
return num * 2;
}
function doubleNum(func, num) {
return func(num);
} // 이 경우에 위의 double 함수를 파라미터 func의 전달인자로 하여 콜백 함수를 사용 할 수 있다.
doubleNum(double, 4); // 8
다른 함수의 결과로 리턴하는 경우는 이 방법을 고안해 낸 하스켈 커리(Haskell curry)의 이름을 딴 커리 함수라고도 한다.
function adder(added) {
return function(num) { // 이 것이 함수를 리턴
return num + added;
}
};
adder(5)(3); // 8
const add3 = adder(3); // 이렇게 함수를 껍질 벗기듯이 사용할 수 있다.
add3(2); // 5
함수를 인자로 받으면서 함수를 리턴하는 두 가지 방법을 함께 사용할 수도 있다.
function double(num) {
return num * 2;
}
function doubleAdder(added, func) {
const doubled = func(added);
return function(num) {
return num + doubled;
};
}
doubleAdder(5, double)(3); // 13
- 내장 고차함수
내장 고차함수란 기본적으로 자바스크립트에 내장된 고차함수를 말한다. 대표적으로 배열 메서드들이 있다. 예를 들어, 배열 메서드인 filter의 경우 배열의 요소 중 특정 조건을 만족하는 요소만 걸러내는 메서드이다. 여기서 걸러내는 기준으로 특정 조건을 filter의 인자로 전달하는데, 이때 조건의 형태가 함수이다. 조건의 형태인 함수를 인자로 받는 것이기 때문에 고차함수라고 할 수 있다. 이 파트에서는 배열 메서드들인 자주 쓰이는 내장 고차함수의 종류와 특징, 사용방법에 대해 알아보도록 하겠다.
1) filter
filter는 위에서도 언급했듯이 특정 조건에 맞게 배열을 걸러내는 역할을 하는 내장 고차함수이다. 먼저 filter를 구성하는 원리를 알기 위해 반복문으로 filter의 기능을 구현해보면 이렇다.
function filterCallback(func, arr) {
// 배열 arr를 반복하여 각 요소마다 func을 적용한다
// 그 결과가 참이면 미리 선언한 변수(result)에 push하여 반환한다
let result = [];
for(let el of arr) {
if (func(el)) {
result.push(el)
}
}
return result;
}
filter를 사용하면 훨씬 간단히 코드를 작성할 수 있다.
let arr = [1, 2, 3, 4];
let output = arr.filter(el => el % 2 === 0)
console.log(output); // [2, 4]
객체를 요소로 하는 배열에서도 사용이 가능한데, 특정 프로퍼티의 특정 값의 조건을 설정하여 필터링하는 것이다.
let users = [
{id: 1, name: "kim"},
{id: 2, name: "Lee"},
{id: 3, name: "Park"}
];
let someUser = users.filter(el => el.id < 3);
console.log(someUser); // [{id: 1, name: "kim"}, {id: 2, name: "Lee"}]
2) map
map은 배열 요소 전체를 대상으로 함수를 호출하고 호출 결과를 배열로 반환해주는 내장 고차함수이다. 특정함수가 인자로 들어가는 경우라고 할 수 있다. map과 유사한 형태가 forEach와 filter인데 세 가지는 용도가 다르다. forEach의 경우에는 각각의 요소에 함수를 적용할 때 사용하고, map은 각각의 요소에 함수를 적용한 결과 값을 도출할 때 사용한다. 그리고 filter는 각각의 요소에 함수를 적용하여 걸러낼 때 사용하는 것이다. map의 기능을 반복문으로 구현하면 이렇다.
function mapCallback(func, arr) {
// TODO: arr 배열을 반복하여 각 요소마다 파라미터 func의 전달인자로 하여 새로운 배열을 반환한다
let result = [];
for (let el of arr) {
result.push(func(el))
}
return result;
}
이 것 역시 map을 사용하여 간단히 작성할 수 있다.
let lengths = ["I", "like", "coding"].map(el => el.length); // [1, 4, 6]
객체를 요소로 가진 배열에서 특정 프로퍼티의 값들만 모아서 배열로 반환할 수도 있다.
let users = [
{id: 1, name: "kim"},
{id: 2, name: "Lee"},
{id: 3, name: "Park"}
];
let names = users.map(el => el.name); // ['Kim", "Lee", "Park"]
3) reduce
reduce는 배열을 기반으로 단일 값을 도출할 때 사용한다. reduce는 앞에서 본 filter와 map과 조금 사용 방법이 다르다. 기본 형태를 살펴보면 이렇다.
let value = arr.reduce(function(accumulator, item, index, array) {
// ...
}, [initial]);
- accumulator – 이전 함수 호출의 결과. initial은 함수 최초 호출 시 사용되는 초깃값을 나타냄(옵션)
- item – 현재 배열 요소
- index – 요소의 위치
- array – 배열
reduce를 많이 사용하는 방법은 총합을 구하는 것이다.
let arr = [1, 2, 3, 4, 5];
let total = arr.reduce((sum, current) => sum + current, 0);
console.log(total); // 15
이렇게 사용하면 sum이라는 변수에 current가 arr배열의 요소를 돌며 한 개씩 더해준다. 초기값으로 0을 설정했으니 0에다가 1을 더해준다. 그리고 그다음으로 sum은 1이 되고 current는 2가 되어 마찬가지로 더해주고 sum에 할당한다. 이 것을 배열을 다 돌 때까지 반복한다. 만약 초기값을 생략하면 초기값은 배열의 첫 번째 요소가 된다. 그래서 처음 반복이 0 + 1이 아닌 1 + 1이 되어 위의 코드의 결과가 16이 될 것이다.
4) forEach
forEach는 배열의 각 요소에 주어진 함수를 실행한다.
["Chi", "Ha", "Yang"].forEach((el, index, array) => {
console.log(`${el} is at index ${index} in ${array}`);
});
// Chi is at index 0 in Chi, Ha, Yang ,
// Ha is at index 1 in Chi, Ha, Yang ,
// Yang is at index 2 in Chi, Ha, Yang
'JavaScript' 카테고리의 다른 글
JS/알고리즘 재귀 알고리즘(recursive algorithms) (0) | 2021.10.08 |
---|---|
JS/Node 객체 지향 JavaScript (0) | 2021.09.27 |
JavaScript 기초9 클로저(closure)와 Spread/Rest문법 (0) | 2021.07.23 |
JavaScript 기초8 자료형(type)과 스코프(scope) (0) | 2021.07.22 |
JavaScript 기초7 객체(Object) (0) | 2021.07.09 |