고차함수 higher order function

일급 객체

First-Class Citizen
자바스크립트의 함수는 일급 객체에 해당한다.

특징

변수에 할당 할 수 있다
다른 함수의 인자로 전달될 수 있다
다른 함수의 결과로 러턴될 수 있다

고차함수란?

함수를 인자(argument)로 받을 수 있거나, 함수의 형태로 리턴할 수 있는 함수
다른 함수(caller)의 인자(argument)로 전달되는 함수를 콜백 함수(callback function)라 한다.
콜백 함수를 전달받은 고차 함수는 함수 내부에서 콜백 함수를 호출 할 수 있다.
'함수를 리턴하는 함수'는 고안자 하스켈 커리의 이름을 따 커리 함수(curry function)라 부른다.
커리 함수는 고차 함수에 포함된다.

다른 함수를 인자로 받는 경우

function double(num) { return num * 2; } function doubleNum(func, num) { return func(num); } /* * 함수 doubleNum은 다른 함수를 인자로 받는 고차 함수입니다. * 함수 doubleNum의 첫 번째 인자 func에 함수가 들어올 경우 * 함수 func는 함수 doubleNum의 콜백 함수입니다. * 아래와 같은 경우, 함수 double은 함수 doubleNum의 콜백 함수입니다. */
JavaScript
복사

함수를 리턴하는 함수

function adder(added) { return function (num) { return num + added; }; } /* * 함수 adder는 다른 함수를 리턴하는 고차 함수입니다. * adder는 인자 한 개를 입력받아서 함수(익명 함수)를 리턴합니다. * 리턴되는 익명 함수는 인자 한 개를 받아서 added와 더한 값을 리턴합니다. */ // adder(5)는 함수이므로 함수 호출 연산자 '()'를 사용할 수 있습니다. let output = adder(5)(3); // -> 8 console.log(output); // -> 8 // adder가 리턴하는 함수를 변수에 저장할 수 있습니다. // javascript에서 함수는 일급 객체이기 때문입니다. const add3 = adder(3); output = add3(2); console.log(output); // -> 5
JavaScript
복사

함수를 인자로 받고, 함수를 리턴하는 경우

function double(num) { return num * 2; } function doubleAdder(added, func) { const doubled = func(added); return function (num) { return num + doubled; }; } /* * 함수 doubleAdder는 고차 함수입니다. * 함수 doubleAdder의 인자 func는 함수 doubleAdder의 콜백 함수입니다. * 함수 double은 함수 doubleAdder의 콜백으로 전달되었습니다. */ // doubleAdder(5, double)는 함수이므로 함수 호출 기호 '()'를 사용할 수 있습니다. doubleAdder(5, double)(3); // -> 13 // doubleAdder가 리턴하는 함수를 변수에 저장할 수 있습니다. (일급 객체) const addTwice3 = doubleAdder(3, double); addTwice3(2); // --> 8
JavaScript
복사
그런데 왜 우리는 고차함수를 사용해야하는거죠?

추상화

복잡한 어떤 것을 압축해서 핵심만 추출한 상태로 만드는 것
추상화를 통해 생산성이 향상
함수는 사고 또는 논리의 묶음으로 자주 반복해서 사용하는 로직을 별도의 함수로 작성하는 것도 추상화의 좋은 사례
값 수준의 추상화: 단순히 값(value)을 전달받아 처리하는 수준
사고의 추상화: 함수(사고의 묶음)를 전달받아 처리하는 수준
고차함수를 통해 보다 높은 수준에서 생각할 수 있고 생산성도 비약적으로 상승

내장 고차 함수

자바스크립트에는 기본적으로 내장된 고차함수가 여러가지 있다.

.filter()

모든 배열의 요소 중에서 특정 조건을 만족하는 요소만 모아 배열로 리턴하는 메소드
걸러내는 기준이 되는 특정 조건은 함수의 형태로 filter 메소드의 인자에 전달된다.
콜백 함수는 boolean값을 리턴하는 함수여야한다.
배열의 요소를 인자로 전달되는 콜백 함수에 전달하고, 콜백 함수는 전달받은 배열의 요소를 받아 함수를 실행한다.
콜백 함수 내부의 조건에 따라 boolean값을 리턴한다.
arr.forEach(callback(currentvalue[, index[, array]])[, thisArg])
JavaScript
복사
Array.prototype.filter = function(func) { const arr = this; const newArr = [] for(let i = 0; i < arr.length; i++) { if (func(arr[i]) === true) { newArr.push(this[i]) } } return newArr; }
JavaScript
복사
const isEven = (num) => { return num % 2 === 0; } let arr = [1, 2, 3]; let result = arr.filter(isEven); console.log(result) // [2]
JavaScript
복사

.forEach()

주어진 콜백 함수를 배열 요소 각각에 대해 실행
arr.forEach(callback(currentvalue[, index[, array]])[, thisArg])
JavaScript
복사
callback : 요소 각각에 대해 실행할 콜백 함수
currentValue : 현재 요소
index : 현재 요소의 인덱스
array : 호출한 배열
let arr = [1, 2, 3]; arr.forEach()
JavaScript
복사

.find()

주어진 콜백 함수를 만족하는 첫 번째 요소의 값을 반환
없다면 undefined 반환
arr.find(callback[, thisArg])
JavaScript
복사
const arr = [1, 2, 3] const found = arr.find(element => element > 2);
JavaScript
복사

.map()

배열 내의 모든 요소 각각에 대하여 주어진 함수를 호출한 결과를 모아 새로운 배열을 반환
JavaScript
복사
const arr = [1, 4, 9, 16]; const map1 = arr.map(x => x * 2); console.log(map1); // [2, 8, 18, 32]
JavaScript
복사

.reduce

배열의 각 요소에 대해 주어진 리듀서 함수를 실행하고 하나의 결과값을 반환
arr.reduce(callback[, initialValue])
JavaScript
복사
const array1 = [1, 2, 3, 4]; const reducer = (accumulator, currentValue) => accumulator + currentValue; // 1 + 2 + 3 + 4 console.log(array1.reduce(reducer)); // 10
JavaScript
복사
배열을 문자열로
function joinName(resultStr, user) { resultStr = resultStr + user.name + ', '; return resultStr; } let users = [ { name: 'Tim', age: 40 }, { name: 'Satya', age: 30 }, { name: 'Sundar', age: 50 } ]; users.reduce(joinName, '');
JavaScript
복사
배열을 객체로
function makeAddressBook(addressBook, user) { let firstLetter = user.name[0]; if(firstLetter in addressBook) { addressBook[firstLetter].push(user); } else { addressBook[firstLetter] = []; addressBook[firstLetter].push(user); } return addressBook; } let users = [ { name: 'Tim', age: 40 }, { name: 'Satya', age: 30 }, { name: 'Sundar', age: 50 } ]; users.reduce(makeAddressBook, {});
JavaScript
복사
reduce로 모든 고차함수 구현 가능
tweets.reduce((acc, cur) => { const isNotUnique = acc.reduce((a, c) => { if (c.username === cur.username) { return true } return a === true ? true : false }, false) return isNotUnique ? acc : [...acc, cur] }, [])
JavaScript
복사

.sort()

배열의 요소를 적절한 위치에 정렬한 후 그 배열을 반환
arr.sort([compareFunction])
JavaScript
복사
const months = ['March', 'Jan', 'Feb', 'Dec']; months.sort(); console.log(months); // expected output: Array ["Dec", "Feb", "Jan", "March"]
JavaScript
복사
function compare(a, b) { if (a를 b보다 낮은 색인으로 정렬) { return -1; } if (b를 a보다 낮은 색인으로 정렬) { return 1; } // a must be equal to b return 0; }
JavaScript
복사

.some

배열 안의 요소 중 하나라도 주어진 판별 함수를 통과하는지 테스트한다.
리턴값은 boolean, 빈 배열에서 호출하면 무조건 false 반환
const array = [1, 2, 3, 4, 5]; const even = (element) => element % 2 === 0; console.log(array.some(even)); // true
JavaScript
복사

.every

배열 안의 모든 요소가 주어진 판별 함수를 통과하는지 테스트
리턴값은 boolean, 빈 배열에서 호출하면 무조건 true
const isBelowThreshold = (currentValue) => currentValue < 40; const array1 = [1, 30, 39, 29, 10, 13]; console.log(array1.every(isBelowThreshold)); // true
JavaScript
복사

.flat

모든 하위 배열 요소를 지정한 깊이까지 재귀적으로 이어붙인 새로운 배열 생성
const arr2 = [1, 2, [3, 4, [5, 6]]]; arr2.flat(); // [1, 2, 3, 4, [5, 6]] const arr3 = [1, 2, [3, 4, [5, 6]]]; arr3.flat(2); // [1, 2, 3, 4, 5, 6] const arr4 = [1, 2, [3, 4, [5, 6, [7, 8, [9, 10]]]]]; arr4.flat(Infinity); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
JavaScript
복사

.findIndex

콜백 함수를 만족하는 첫번째 요소의 인덱스 반환
MapReduce 학습하기 (MapReduce Model)
자바스크립트에서 커링(currying)과 클로저(closure)의 차이 이해하기 (js closure vs curry)
선언형 프로그래밍(declarative programming)과 절차형 프로그래밍(imperative programming)의 차이를 배열 메소드를 통해 이해하기 (js imperative vs declarative)
함수의 조합(function composition)에 대해 학습하기 (javascript function composition)