고차함수에 대한 전반적인 개념
→ 자바스크립트에도 특별한 대우를 받는 일급 객체(first-class citizen) : 함수
→ 자바스크립트에서 함수는 아래와 같이 특별하게 취급
1. 변수에 할당(assignment) 할 수 있다.
2. 다른 함수의 인자(argument)로 전달될 수 있다.
3. 다른 함수의 결과로서 리턴될 수 있다.
→ 함수를 변수에 할당할 수 있기 때문에, 함수를 배열의 요소나 객체의 속성값으로 저장할 수 있습니다.
→ 함수를 데이터(string, number, boolean, array, object)를 다루듯이 다룰 수 있다는 걸 의미
→ 변수에 함수를 할당 : 함수 표현식, 화살표 함수
→ 함수 표현식(function expression)은 함수 선언식(function declaration)과 다르게 호이스팅(Hoisting)이 적용되지 않습니다.
/ 호이스팅은 선언된 위치에 관계없이 어디서든 함수를 사용할 수 있도록 합니다.
/ 파싱과정에서 함수 선언을 최상단으로 끌어올려 선언하는 방식(코드의 위치는 유지)
/ 코드가 실행되는 과정에서 함수 선언부를 코드의 최상단으로 끌어올리는 것처럼 보이게 합니다.
→ 함수 선언식의 호이스팅에 지나치게 의존하면, 코드의 유지 보수가 쉽지 않습니다
→ 함수 선언식은 어느 위치에나 함수를 선언할 수 있고, 함수의 실행 위치도 중요하지 않습니다
→ 함수 표현식은 함수의 할당과 실행의 위치에 따라 결과가 달라지기 때문에, 코드의 위치를 어느 정도 예측할 수 있습니다.
→ 함수는 변수에 저장된 데이터를 인자로 받거나, 리턴 값으로 사용할 수 있습니다.
→ 함수도 변수에 저장될 수 있기 때문에 함수를 인자로 받거나, 리턴 값으로 사용할 수 있습니다
고차 함수란
→ 고차 함수(higher order function)는 함수를 인자(argument)로 받을 수 있고, 함수의 형태로 리턴할 수 있는 함수입니다.
→ 함수 내부에서 변수에 함수를 할당할 수 있으며 함수는 이 변수를 리턴할 수 있습니다.
→ 함수 내부에서 변수에 할당하지 않고 함수를 바로 이용할 수 있습니다.
→ 고차 함수에 함수를 인자로 전달하고, 고차 함수는 함수 자체를 리턴
→ 콜백함수 : 다른 함수(caller)의 인자(argument)로 전달되는 함수를 콜백 함수(callback function)라고 합니다.
→ 콜백 함수의 이름은, 어떤 작업이 완료되었을 때 호출하는 경우가 많아서, 답신 전화를 뜻하는 콜백이라는 이름이 붙여졌습니다.
→ 콜백 함수를 전달받은 고차 함수는, 함수 내부에서 이 콜백 함
수를 호출(invoke) 할 수 있습니다
→ caller는 조건에 따라 콜백 함수의 실행 여부를 결정할 수 있습니다 (caller는 콜백함수를 파라미터로 받는 함수)
→ 커링함수 : '함수를 리턴하는 함수'
→ 커링 함수라는 용어를 사용하는 경우에는, 고차 함수란 용어를 '함수를 인자로 받는 함수'에만 한정해 사용
→ 정확하게 구분하자면, 고차 함수가 커리 함수를 포함, 즉 '함수를 리턴하는 함수'와 '함수를 인자로 받는 함수' 모두, 용어를 고차 함수로 사용
→ 클로저의 한 유형이 커링함수(둘이 다른 개념이 아님)
--------------------------------------------------------------
* map() 메서드는 배열 내의 모든 요소 각각에 대하여 주어진 함수를 호출한 결과를 모아 새로운 배열을 반환합니다. / 기존 배열은 그대로 유지된다
ex) 배열의 각 요소에 함수적용, 그 결과값을 다시 요소로 저장
const array1 = [1, 4, 9, 16];
// pass a function to map
const map1 = array1.map(x => x * 2);
console.log(map1);
// expected output: Array [2, 8, 18, 32]
--------------------------------------------------------------
* filter() 메서드는 주어진 함수의 테스트를 통과하는 모든 요소를 모아 새로운 배열로 반환합니다. / 기존 배열은 그대로 유지
ex) 각 요소에 함수 적용, 반환 값이 true인 element만 요소로 다시 재 할당해 배열 return
// 함수 표현식
const isEven = function (num) {
return num % 2 === 0;
};
let arr = [1, 2, 3, 4];
// let output = arr.filter(짝수);
// '짝수'를 판별하는 함수가 조건으로서 filter 메소드의 인자로 전달됩니다.
let output = arr.filter(isEven);
console.log(output); // ->> [2, 4]
--------------------------------------------------------------
[함수를 인자로 받고, 함수를 리턴하는 경우]
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
----------------------------------------------------------------------------------------------------------------
[함수와 수(num)를 입력받아 num에 함수를 두 번(twice) 적용(apply)한 결과를 리턴]
function applyTwice(func, num) {
return func(func(num));
}
[두 개의 함수를 입력받아 두 함수가 결합된 새로운 함수를 리턴해야 합니다.]
function compose2(func1, func2) {
return function(num) {
return func1(func2(num));
}
}
[함수들을 입력받아 함수들이 입력된 차례대로 결합된 새로운 함수를 리턴해야 합니다.]
function pipe() {
// arguments 객체를 배열로 변환한다.
let argsArr = Array.prototype.slice.call(arguments);
return function(num) {
let temp = num;
for(let i=0;i<argsArr.length;i++) {
temp = argsArr[i](temp)
}
return temp;
}
}
----------------------------------------------------------------------------------------------------------------
[내장된 고차함수]
→ 기본적으로 내장된 고차함수 : 배열 메소드들 중 일부가 대표적인 고차함수에 해당
(ex) filter 메서드 )
→ 배열의 filter 메소드는, 모든 배열의 요소 중에서 특정 조건을 만족하는 요소를 걸러내는 메소드
let arr = [1, 2, 3, 4];
let output = arr.filter(짝수);
// 짝수 자리에 함수가 들어간다. 짝수일 경우에만 true를 반환한다.
console.log(output); // ->> [2, 4]
→ 걸러내는 기준이 되는 특정 조건은 filter 메소드의 인자로 전달
→ 전달되는 조건은 함수의 형태입니다
→ filter 메소드는, 걸러내기 위한 조건을 명시한 함수를 인자로 받기 때문에 고차함수
→ filter 메소드는 배열의 요소를, 인자(파라미터)로 전달되는 콜백 함수에 다시 전달
→ 콜백 함수는 전달받은 배열의 요소를 받아 함수를 실행하고, 콜백 함수 내부의 조건에 따라 참(true) 또는 거짓(false)을 리턴 / 여기서 콜백 함수는 filter메서드의 파라미터
→ 자바스크립트 배열 메소드 중 고차 함수 : forEach, find, filter, map, reduce, sort, some, every
→ filter 메소드에 들어가는 콜백 함수는 truthy 또는 falsy를 리턴할 수 있지만
filter 메소드에 들어가는 콜백 함수는 Deep equality를 통해 조건을 명확하게 밝히는 걸 권장
----------------------------------------------------------------------------------------------------------------
- reduce
→ reduce() 메서드는 배열의 각 요소에 대해 주어진 리듀서(reducer) 함수를 실행하고, 하나의 결과값을 반환합니다.
→ reduce는 이렇게 여러 데이터를, 하나의 데이터로 응축(reduce)할 때 사용합니다.
ex) reduce() 메서드에 초기값을 지정해주면 초기값,배열element순서대로 함수가 실행되며 결과값을 누산한다
/ 초기값이 없는 경우 초기값은 배열의 0번째 인덱스로 지정되어 배열 element들만 함수가 실행되어 결과값을 누산한다.
const array1 = [1, 2, 3, 4];
const reducer = (accumulator, currentValue) => accumulator + currentValue;
// 1 + 2 + 3 + 4
console.log(array1.reduce(reducer));
// expected output: 10
// 5 + 1 + 2 + 3 + 4
console.log(array1.reduce(reducer, 5));
// expected output: 15
배열을 문자열로
배열을 객체로 (1개의 객체로 합친다)
----------------------------------------------------------------------------------------------------------------
[정수를 요소로 갖는 배열과 정수(num)를 입력받아 num을 배열에 추가하고 정렬한다고 가정할 경우, num의 인덱스를 리턴해야 합니다.]
function getIndex(arr, num) {
// TODO: 여기에 코드를 작성합니다.
if(arr.length === 0) return 0;
else {
arr.push(num);
// num보다 작은 경우 : 앞에 위치
-> arr길이 - num보다 크거나 같은 배열길이 = num앞 el개수
// num위치는 'arr길이 - num보다 크거나 같은 배열길이 + 1' 이므로
인덱스는 arr길이 - num보다 크거나 같은 배열길이
// filter는 num과 같거나 큰 element들만 걸러냄
let filterFunc = function(args) {
return num <= args;
}
// num보다 크거나 같은 배열
let tempArr = arr.filter(filterFunc);
// 현재 num의 위치 : arr길이 - num보다 크거나 같은 배열길이
return arr.length - tempArr.length;
}
}
[객체와 키를 입력받아 키에 해당하는 값이 배열인 경우, 100 보다 작은 요소들만 갖는 배열을 리턴해야 합니다.]
function lessThan100(number) {
return number < 100;
}
function getElementsLessThan100AtProperty(obj, property) {
// TODO: 여기에 코드를 작성합니다.
if(property in obj && Array.isArray(obj[property])){
// 이 배열에는 boolean값도 포함이 된다.(boolean은 number와 대소비교 가능)
let temp = obj[property].filter(lessThan100);
// 새로 filter함수 작성(return 반드시 붙일것!)
return temp.filter(function(el){
return typeof el === 'number';
});
}
else return [];
}
----------------------------------------------------------------------------------------------------------------
- 추상화
→ 추상화는 복잡한 어떤 것을 압축해서 핵심만 추출한 상태로 만드는 것이 추상화입니다. / 추상화를 이용하면, 효율적이고 편하게 생각할 수 있기 때문
→ 예를 들어 우리는 '-1'이라는 문자를 보고, -1은 0보다 1만큼 작은 수라고 설명할 수 있습니다 / '-1'을 표현하는 현실의 방법은 존재하지 않습니다
→ ex) 브라우저 창에 주소를 입력했을 때, 어떤 일이 일어나는지 정확하게 알고 있지 않아도 우리는 그저 주소창에 올바른 주소를 입력하면, 브라우저가 해당 사이트를 보여 준다는 것만 알고 있어도 된다
/ 입력한 내용을 전파하고, 어디 서버로 갔다가 다른 서버로 가는 등 그런 복잡한 내용은 사용자가 알필요가 없다.
→ 자동차의 시동 버튼, 자료를 정리하는 엑셀, 지하철/버스를 타기 위한 교통 카드도 추상화의 결과
→ 자바스크립트를 비롯한 많은 프로그래밍 언어 역시, 추상화의 결과
→ 컴퓨터를 구성하는 장치(중앙처리장치, CPU; Central Processing Unit)는 0과 1만 이해하며 개발자는 크롬 개발자 도구의 콘솔(console) 탭에서 다음의 코드를 입력했을 때, 어떤 과정을 거쳐 10이 출력되는지 몰라도 10을 출력할 수 있습니다
/ 복잡한 것들은 크롬의 자바스크립트 해석기(엔진)가 대신해 주기 때문입니다.
/ 해당 문법을 기계어(0,1로 구성)로 해석해서 컴퓨터에 명령어 전달 -> 추상화로 생략됨
/ 컴퓨터의 내부 구조에 대한 고민이 추상화로 해결
*** 추상화 = 생산성(productivity)의 향상
→ 로그램을 작성할 때, 자주 반복해서 사용하는 로직은 별도의 함수로 작성
/ 함수의 내부 내용은 몰라도 어떠한 것을 반환하는지만 알면 함수의 인자를 맞춰 가져다가 사용하면 된다 : 추상화
→ 함수를 통해 얻은 추상화를, 한 단계 더 높인 것이 고차 함수
ex) getAverage 함수는 값(배열)을 전달받아, 이 값을 가지고 복잡한 작업을 수행
→ 이는 값 수준에서의 추상화
→ 함수 = 값을 전달받아 값을 리턴한다 = 값에 대한 복잡한 로직은 감추어져 있다 = 값 수준에서의 추상화
→ 고차 함수는 이 추상화의 수준을 사고의 추상화 수준으로 끌어올립니다.
/ 값 수준의 추상화: 단순히 값(value)을 전달받아 처리하는 수준
/ 사고의 추상화: 함수(사고의 묶음)를 전달받아 처리하는 수준
*** 고차함수 = 함수를 전달받거나 함수를 리턴한다 = 사고(함수)에 대한 복잡한 로직은 감추어져 있다 = 사고 수준에서의 추상화
[여러개의 함수를 인자로 받아서 처리하는 함수(spread문법 사용)]
→ function compose(...funcArgs) {
// compose는 여러 개의 함수를 인자로 전달받아 함수를 리턴하는 고차 함수입니다.
// compose가 리턴하는 함수(익명 함수)는 임의의 타입의 data를 입력받아,
return function (data) {
// funcArgs의 요소인 함수들을 차례대로 적용(apply)시킨 결과를 리턴합니다.
let result = data;
for (let i = 0; i < funcArgs.length; i++) {
result = funcArgs[i](result);
}
return result;
};
}
[객체 배열인 경우 reduce 사용시 특이사항]
// reduce
// sum에 0 초기화 필요
// -> 만약 객체로 이루어진 배열인 경우 초기화가 없을 경우 객체가 sum에 초기화가 된다.
return temp.reduce(function(sum, el){
return sum + el.score;
},0);
→ 객체 배열인 경우 각 element에 접근하기 위해서 map()을 이용해서 접근 후
‘return 파라미터;’ 하면 각 element를 수정후 변경된 element들로 구성된 배열이 반환
----------------------------------------------------------------------------------------------------------------
[학생의 정보가 담긴 객체를 요소로 갖는 배열을 입력받아 아래 조건에 맞게 변형된 배열을 리턴해야 합니다.
- 남학생들의 정보는 리턴하는 배열에서 제외합니다.
- 'grades' 속성값은 평균값(number 타입)으로 바꿉니다.]
인자 1 : students
- 객체를 요소로 갖는 배열
- arr[i]는 'name', 'gender' 등의 속성을 갖는 객체
- 'grades' 속성은 number 타입을 요소로 갖는 배열
- 'grades' 속성이 빈 배열인 경우는 없다고 가정합니다.
입출력 예시
let studentList = [
{
name: 'Anna',
gender: 'female',
grades: [4.5, 3.5, 4],
},
{
name: 'Dennis',
gender: 'male',
country: 'Germany',
grades: [5, 1.5, 4],
},
{
name: 'Martha',
gender: 'female',
grades: [5, 4, 4, 3],
},
{
name: 'Brock',
gender: 'male',
grades: [4, 3, 2],
},
];
let output = studentReports(studentList);
console.log(output); // -->
[
{ name: 'Anna', gender: 'female', grades: 4 },
{ name: 'Martha', gender: 'female', grades: 4 },
];
[코드]
function studentReports(students) {
// TODO: 여기에 코드를 작성합니다.
// 1. 여학생나누기
let femaleStudent = students.filter(function(el){
return el.gender === 'female';
});
// 2. 평균값 구하기
return femaleStudent.map(function(el){
// 3. 평균값을 넣어줌
// 배열의 각 객체요소에 접근해서 해당 객체의 속성값 변경
el.grades = el.grades.reduce(function(sum,avgs){
// 내부함수에서 외부함수에서 사용하는 매개변수를 사용할 수 있음(클로저)
return sum+avgs / el.grades.length;
},0);
return el
});
}
----------------------------------------------------------------------------------------------------------------
→ 2차원 배열을 1차원 배열로 변경하는 경우
let oneDimensionArr = 2차원배열명.reduce(function(result,args){
return result = [...result,...args];
},[]);
→ spread 문법사용, 초기값은 ‘[]’로 설정하여 배열로 합친다.
→ 초기값은 내가 원하는 형태의 초기값을 주는 것이 좋음
ex) 배열은 [] , number은 0, string은 ‘’, 객체는 {}
→ reduce()를 이용하여 배열의 최대값을 구하는 식
----------------------------------------------------------------------------------------------------------------
고차함수는 다음과 같은 특징을 가지고 있습니다.
- 다른 함수를 인자로 받는 경우
- 함수를 리턴하는 경우
- 위 두가지 모두에 해당하는 경우
unshift는 위 세가지에 모두 해당하지 않기 때문에 고차함수가 아닙니다.
Array.prototype.find / Array.prototype.reduce / Array.prototype.filter 는 고차함수