일급객체는 JavaScript에서 특별한 대우를 받는다. 일급 객체 중 하나가 함수다.
JavaScript에서 함수 취급
고차함수는 함수를 전달인자로 받을 수 있고, 함수를 리턴할 수 있는 함수다. 또한 함수를 인자로 받고, 함수를 리턴할 수도 있다.
이때 다른 함수의 전달인자로 전달되는 함수를 콜백 함수(callback function)라고 한다.
함수 내부에서 이 콜백 함수를 호출할 수도 있고, 조건에 따라 콜백 함수의 실행 여부를 결정할 수도, 아예 호출하지 않을 수도, 여러번 실행할 수도 있다.
1. 다른 함수를 인자로 받는 경우
function double(num) {
return num * num;
}
function doubleNum(func, num) { // 다른 함수를 인자로 받기 때문에 고차 함수
return func(num); // func 함수는 doubleNum의 콜백 함수
}
let output = doubleNum(double, 4);
console.log(output) // => 8;
2. 함수를 리턴하는 경우
function adder(added) { // 함수 adder는 익명 함수를 리턴하는 고차 함수.
return function (num) {
return num + added;
}
}
let output = adder(5)(3); // => 8; adder(5)는 함수를 리턴하기 때문에 "()"를 쓸 수 있다.
const add3 = adder(3); // 함수를 리턴하는 adder(3) 함수는 add3이라는 변수에 저장했다.
// JavaScript에서 함수는 일급 객체이기 때문에 변수에 저장할 수 있다.
output = add3(2);
3. 함수를 인자로 받고, 함수를 리턴하는 경우
function double(num) {
return num * 2
}
function doubleAdder(added, func) { // doubleAdder 는 함수를 전달인자로 받는 고차함수.
//인자 func는 함수 doubleAdder의 콜백 함수.
const doubled = func(added);
return function(num){
return num + doubled;
}
}
doubleAdder(5, double)(3); // => 13; doubleAdder(5, double)는 함수이므로 "()"로 함수 호출을 할 수 있다.
const addTwice3 = doubleAdder(3, double); // doubleAdder가 리턴하는 함수는 일급 객체기 때문에 변수에 저장할 수 있다.
addTwice3(2); // => 8;
JavaScript에 기본적으로 내장된 고차 함수들이 있다. 그 중 메서드들 중 일부가 대표적인 고차 함수다.
모든 배열의 요소 중에서 특정 조건을 만족하는 요소를 걸러내는 메서드다.
let arr = [1, 2, 3, 4];
let output = arr.filter(function(el) {
if(el % 2 === 0){
return el;
}
});
console.log(output) // => [2, 4];
배열의 요소들을 조건식 함수에 넣어서 true면, 요소를 배열에 넣고, false면 요소를 배열에서 뺀다. 이때 만들어진 배열은 기존 배열을 건드리지 않는 새로운 배열이다.
하나의 데이터를 다른 데이터로 매핑(mapping) 할 때 사용한다.
const arr = [{key:10}, {key:20}, {key:30}]
const findValues = function(value) {
return value["key"];
}
const values = arr.map(findValues);
console.log(values) //=> [10, 20, 30]
const arr = [{key:10}, {key:20}, {key:30}]
return arr.reduce(function(acc, cur) { // acc는 누적값, cur는 현재값.
return acc + cur // 더해서 누적하는 거 말고도 누적값과 현재값을 비교하는 다양한 함수를 만들 수 있다.
}, 0);
reduce 함수의 색다른 사용법
초기값 설정으로 누적하여 배열을 문자열로, 배열을 객체로 만들 수도 있다.
const maxValueKey = arr.reduce(function(acc, cur) {
if(acc["key"] < cur["key"]) {
return cur;
} else if(acc["key"] >= cur["key"]) {
return acc;
}
});
console.log(maxValueKey); // => {key: 30}
.map(), .filter() 등등 많은 고차 함수들이 있다. 우리는 이 메서드들이 어떻게 생겨먹었는지 정확하게는 모르지만 어떤 값을 넣으면 어떤 값이 리턴되는지 알고 있다. 메서드의 복잡한 로직은 감춰져 있다.
메서드는 고차 함수를 추상적으로 표현했다고 볼 수 있다. 고차 함수를 통해 추상화의 수준을 높일 수 있고 그만큼 생산성도 높일 수 있다.
어떤 데이터를 넣었을 때, 함수 하나에 넣어서 사용할 수 있다.
function 평균나이값(data) {
const onlyMales = data.filter() // 남자들만 모아놓은 배열
const onlyMaleAges = onlyMales.map() // 남자들 나이만 모아놓은 배열
const sumOfAges = onlyMaleAges.reduce() // 나이만 모아놓은 배열을 누적시켜 더하기
return sumOfAges/ onlyMales.length
}
이렇게 만들면 순차적으로 만들 수 있다. 하지만 평균 나이만 구할 수 있는 작업만 수행하는 함수일 뿐이다.
하지만 추상화는 고차 함수를 통해 함수를 순서대로 결합하는 고차함수가 있다. 각각의 작업은 별도의 함수로 분리되어 사용 가능하며 또한, 고차 함수의 콜백 함수로써도 사용 가능하다.
function 남자만리턴(data) {
return data.filter()
}
function 나이만리턴(data) {
return data.map()
}
function 나이평균리턴(data) {
const sum = data.reduce()
return sum / data.length
}
function compose(...funcArgs) {
return function (data) {
let result = data;
for(i = 0; i < funcArgs.length; i++) {
result = funcArgs[i](result);
}
return result;
}
}
const getAverageAgeOfMale = compose(
남자만리턴,
나이만리턴,
나이평균리턴
)
const result = getAverageAgeOfMale(data);
console.log(result);
이제 진짜 어려워졌다. 자세히 봐야 한다. 보는 것도 힘든데 만드는 것은 더 어렵겠지? 천천히 함수를 따라가며 해보자.