고차함수에서 우리가 배워야 될 것
JavaScript에서 일급 객체는 특별한 대우를 받습니다. 대표적인 일급 객체가 함수입니다.
함수를 변수에 할당할 수 있기 때문에, 함수를 배열의 요소나 객체의 속성값으로 저장할 수 있습니다.
ex) 변수에 함수를 할당하는 경우 (함수 표현식)
const square = function(num) {
return num * num;
};
result = square(7);
console.log(result); // 49 출력
위와 같은 함수 표현식 (function expression)은 함수 선언식 (function declaration)과 다르게 호이스팅 적용이 안됩니다.
따라서 함수 선언식의 호이스팅에 지나치게 의존한다면 코드의 유지 보수가 어려워질 수 있습니다. 호이스팅을 제외하면 함수 선언식과 함수 표현식은 크게 차이가 없습니다.
고차 함수는 함수를 인자로 받을 수 있고, 함수의 형태로 리턴할 수 있는 함수입니다. 따라서 함수 내부에서 변수에 함수를 할당할 수 있습니다. 그리고 함수를 통해 변수를 반환할 수 있습니다.
(고차 함수에서는 함수를 인자로 전달하고, 고차 함수는 함수 자체를 반환합니다. 변수가 빠졌을 뿐 동일하게 동작합니다.)
다른 함수(caller)의 인자(argument)로 전달되는 함수를 콜백 함수(callback function)이라고 합니다. 특정 작업이 끝났을 때 호출하는 함수입니다.
콜백 함수를 전달받은 고차 함수는, 함수 내부에서 콜백 함수를 호출(invoke)할 수 있습니다. 함수 (caller)는 조건에 따라 콜백 함수의 실행 여부를 결정할 수 있습니다. 호출하지 않을 수 있고, 여러 번 실행할 수도 있습니다.
ex) 함수를 인자로 받는 함수 (고차 함수)
function double(num) {
return num * 2;
}
function doubleNum(func, num) {
return func(num);
}
let result = doubleNum(double, 4);
console.log(result) // 8;
ex) 함수를 리턴하는 경우
function adder(added) {
return function(num) {
return num + added;
};
}
let result = adder(5)(3);
console.log(result); // 8 출력
const add3 = adder(3); // adder가 함수에 변수 3을 저장해놓고
let result2 = add3(2); // 저장된 변수 3을 added에 넣은 상태에서 function(num)에 2를 삽입한다.
console.log(result2); // 따라서 결과값은 5가 나온다.
ex) 함수를 인자로 받고, 함수를 리턴하는 경우
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 출력
// added에 5를 넣고, func에 double을 받아온다.
// doubled = double(5)가 된다.
// double return 값은 10이다.
// return function(3)을 통해
// return의 최종 결과값은 3 + 10 이다.
const addTwice3 = doubleAdder(3, double);
addTwice3(2); // 8 출력
배열 내장 고차함 수인 filter에 대해서 배워야 합니다.
filter에 기반한 나머지 고차함수
abstraction(추상화)에 대한 이해가 필요합니다.
JavaScript에는 다양한 고차함수가 존재합니다. 그 중 배열 메소드 중 일부가 대표적인 고차함수에 해당합니다. 가장 기본적인 filter에 대한 이해 후 다음으로 넘어가겠습니다.
filter 메소드는 걸러내기 위한 조건을 명시한 함수라고 생각하면 됩니다.
ex)
let arr = [1,2,3,4];
arr.filter = function(arr, func){
const newArr = [];
for(let i = 0; i < arr.length; i++){
if(func(arr[i]) === true){
newArr.push(this[i]);
}
}
return newArr;
};
ex) 실제 예시를 활용한 filter 1번
const isEven = function(num) {
return num % 2 === 0;
};
let arr = [1,2,3,4];
let result = arr.filter(isEven);
console.log(result); // [2,4];
ex) 실제 예시를 활용한 filter 2번
const isLetterFive = function(str){
return str.length <= 5;
};
arr = ["Hello", "Code", "States", "Happy", "Hacking"];
let result = arr.filter(isLetterFive);
console.log(result); // ["Hello", "Code", "Happy"] 출력
모든 요소에게 동일한 행동을 준 값에 대하여 모두 반환합니다.
행동은 개발자가 함수로 직접 작성해야 합니다. (기존 배열은 원본 유지가 됩니다.)
원하는 행동에 따라서 결과가 달라집니다.
ex) Map 예시
let arr = [1,2,3];
let result = arr.map(function(ele){
return ele * 2
});
return result; // [2,4,6] 출력
ex) map 실제 코드
const movies = [
{
id: 1,
bookType: 'Movie',
title: 'Mission Impossible',
subtitle: 'Powerful',
createdAt: '2010-10-10',
genre: 'Action',
artist: 'Cruz',
AverageScore: 9.55,
},
{
id: 2,
// .. 이하 생략
},
// ... 이하 생략
];
const findSubtitle = function(movies) {
return movies.subtitle;
}
const subtitles = movies.map(findSubtitle);
// 각 영화들의 subtitle을 출력할 수 있다.
filter는 모든 요소 중 내가 원하는 값만 filtering 하여 반환합니다.
코드는 개발자가 직접 작성해야하고, 함수로 작성하여 인자로 받습니다.
기존 배열은 유지됩니다.
원하는 filtering에 따라서 결과가 달라집니다.
ex) filter 예시
let arr = [1,2,3];
let result = arr.filter(function(ele){
return ele % 2 === 0
});
// 1,3 출력
ex) filter 실제 예시
const movies = [
{
id: 1,
Type: 'movies',
title: 'Mission Impossible',
subtitle: 'Festival',
createdAt: '2010-10-10',
genre: 'Action',
artist: 'Cruz',
averageScore: 9.55,
}
{
id: 2,
// ... 이하 생략
},
// ... 이하 생략
];
const isCreatedAt2010 = function(movies){
const fullYear = new Data(movies.createdAt).getFullYear();
return fullYear === 2010;
};
const filteredMovies = movies.filter(isCreatedAt2010);
// 2010년에 만들어진 영화들 출력
reduce는 배열을 하나의 값으로 만들어줍니다.
reduce는 초기값을 설정할 수 있습니다. (설정을 안할경우 배열의 첫 번째 값이 초기값이 됩니다.)
초기값은 누적값에 저장됩니다.
이후 배열의 끝 까지 누적값에 저장이 됩니다. (저장이 될 때는 개발자가 원하는 행동에 따라 값이 바뀝니다.)
마지막에는 누적값의 값을 반환하게 됩니다.
ex) reduce 초기값 설정이 없는 예시
let arr = [1,2,3];
let result = arr.reduce(function(acc, cur, idx) {
acc += cur;
return acc;
});
result; // 6
ex) reduce 초기값이 설정된 예시
let arr = [1,2,3];
let result = arr.reduce(function(acc, cur, idx){
acc += cur;
return acc;
}, 1); // 1로 초기값을 설정
result; // 결과는 7을 출력한다.
ex) reduce 실제 코드
const movies = [
{
id: 1,
Type: 'movies',
title: 'Mission Impossible',
subtitle: 'Festival',
createdAt: '2010-10-10',
genre: 'Action',
artist: 'Cruz',
averageScore: 9.55,
}
{
id: 2,
// ... 이하 생략
},
// ... 이하 생략
];
const scoreReducer = function(sum, movies) {
return sum + movies.averageScore;
};
let initialValue = 0;
const moviesAvgScore = movies.reduce(scoreReducer, initialValue) / movies.length;
배열을 문자열로 출력할 수 있습니다.
function joinName(resultStr, user) {
resultStr = resultStr + user.name + ', ';
return resultStr;
}
let users = [
{ name: 'KJ', age: 40 },
{ name: 'SJ', age: 30 },
{ name: 'SH', age: 50 }
];
console.log(users.reduce(joinName, '')); // KJ, SJ, SH, 출력
function makeAddressBook(addressBook, user){
let firstLetter = user.name[0];
if(firstLetter in addressBook){
addressBook[firstLetter].push(user); // firstLetter 가 있는 경우
} else {
addressBook[firstLetter] = []; // firstLetter 가 없는 경우
addressBook[firstLetter].push(user);
}
return addressBook;
}
let users = [
{ name: 'KJ', age: 40},
{ name: 'SJ', age: 30},
{ name: 'UH', age: 50}
];
console.log(users.reduce(makeAddressBook, {}));
//{
// K: [ { name: 'KJ', age: 40} ],
// S: [ { name: 'SJ', age: 30} ],
// U: [ { name: 'UH', age: 50} ],
//} 출력
추상화(abstraction) 때문입니다. 복잡한 어떤 것을 압축해서 핵심만 추출한 상태로 만드는 것이 추상화입니다. 예를들면 우리가 1부터 5까지 더했을 때 어떤 과정을 거쳐서 15라는 값이 나오는지 몰라도 결과값을 알 수 있습니다.
ex)
function sum(num1, num2){
return num1 + num2;
}
const result = sum(2, 5);
console.log(result); // 7 출력
이런 문제는 컴퓨터의 추상화를 통해 해결할 수 있습니다.
추상화 = 생산성 향상
함수를 통해 얻은 추상화를 한 단계 높인 것이 고차 함수입니다.
고차 함수는 이 추상화의 수준을 사고의 추상화 수준으로 끌어올립니다.
추상화의 수준이 높아지는 만큼 생산성은 향상됩니다.
const data = [
{
gender: 'male',
age: 24,
},
{
gender: 'male',
age: 25,
},
{
gender: 'female',
age: 27,
},
{
gender: 'female',
age: 22,
},
{
gender: 'male',
age: 29,
},
];
// 남자만 있는 배열을 만들어보자
function getAverageOfMaleAtOnce(data) {
const onlyMales = data.filter(function (el) {
return el.gender === 'male';
});
const numOfMales = onlyMales.length;
const onlyMaleAges = onlyMales.map(function (el) {
return el.age;
}); // 오직 남라고 구성된 배열에서 나이를 배열로 리턴할 수 있다.
const sumOfAges = onlyMales.reduce(function(acc, cur){
acc += cur
return acc;
}, 0); // 오직 남자들의 나이만을 더했을 때
return sumOfAges / numOfMales;
// 남자들의 평균 나이
}
순차적으로 원하는 작업을 수행할 수 있습니다. 남성의 평균 나이를 구할 때 다양한 방법 을 통해서 결과를 구할 수 있습니다.
고차 함수의 종류