일반적으로 반복문을 사용하려면 반복문을 어떻게 실행할지에 초점을 맞춰서 구현하게 된다. 따라서 반복문의 시작점 및 초기값인 counter, 반복문 멈추는 기준이 되는 exit condition과 끝나는 조건에 도달할 때까지 counter를 증가시키는 iterator를 가지게 된다.
즉, 반복문을 구현하려면 이 세 가지가 모두 필요하다.
이와 반대로 고차 함수를 이용한 반복문은 어떻게 실행할지 보다는 무엇을 할지에 초점을 맞춘다. 각각의 반복을 구체적으로 어떻게 진행할지를 명시하기보다는 원하는 결과를 얻기 위한 과정만을 제시한다.
이 둘의 차이점이 더 와 닿을 수 있도록 앞으로의 예제에서 일반적인 반복문(for, while)과 고차 함수를 이용한 반복문을 둘 다 보여줄 것이다.
그럼 이제, 알고 있으면 편한 고차 함수들을 알아보자.
자바스크립트에서 자주 사용되는 고차 함수들은 주로 배열에 내재되어 있는 메소드들이다.
forEach( )
배열의 각 원소를 순서대로 모두 돌면서 함수를 실행한다. 반환되는 값은 없다.
EX: 특정 배열에 있는 원소를 다른 배열에 복사.
const fruits = ['watermelon', 'blueberries', 'strawberry', 'raspberry'];
const copyFruits = []
// before
for (let i = 0; i < fruits.length; i++){
copyFruits.push(fruits[i]);
}
// after
fruits.forEach((fruit) => copyFruits.push(fruit));
for문은 continue나 break로 반복을 제어할 수 있지만 forEach는 throw(예외)를 발생시키지 않으면 중간에 반복을 종료할 수 없다.
하지만 forEach문과 예외 처리를 사용하면 코드가 더 복잡해진다. 그렇기 때문에, 조건을 만족할 때까지만 반복시켜야 한다면 기존의 for문이나 some() 함수를 사용하는 것이 더 적절하다.
EX: 배열의 값이 2인 경우만 출력. 비슷하게 배열 중 일부값을 찾아서 뭔가를 수행하고자 하는 상황에 해당한다.
// 예외 처리 없이
const numbers = [1, 2, 3, 4, 5];
numbers.forEach((number) => {
if (number == 2){
console.log(number);
break;
}
});
// 예외 처리를 이용한 forEach 반복 종료
const errorBreak = new Error('Break');
const numbers = [1, 2, 3, 4, 5];
try {
numbers.forEach((number) => {
if (number == 2){
console.log("number 2!");
throw errorBreak;
}
})
} catch (e) {
if (e != Break)
throw Break;
}
filter ( )
콜백 함수에 의해 제공된 테스트를 통과한 모든 원소를 가진 새로운 배열을 반환한다.
EX: 성인인 사람(18세 이상)의 사람만 필터링한다
const people = [
{ name: 'Peter', age: 16},
{ name: 'Mark', age: 18},
{ name: 'John', age: 27},
{ name: 'Jane', age: 14},
{name: 'Ester', age: 24}
];
const legalAge = [];
// before
for (let i = 0; i < persons.length; i++){
if(persons[i].age >= 18){
fullAge.push(persons[i];
}
}
// after
legalAge = persons.filter(person => person.age >= 18);
sort( )
지정된 함수에 따른 순서로 배열을 정렬한다. 함수가 지정되지 않았을 때에는 배열의 원소들을 문자로 반환해서 유니코드에 따라서 정렬한다.
EX: 문자열이 들어있는 배열을 정렬한다.
const fruits = ["strawberry", "watermelon", "orange", "blueberry", "banana", "apple"];
fruits.sort();
console.log(fruits);
// ["apple", "banana", "blueberry", "orange", "strawberry", "watermelon"]
const numbers = [1, 30, 4, 21, 10000, 225, 335];
numbers.sort();
console.log(numbers);
// [1, 1000, 21, 225, 30, 335, 4]
sort() 함수는 원소들을 유니코드로 전환시켜 정렬하기 때문에 숫자로 이루어진 배열을 정렬할 때는 정렬하는 기준을 정해주는 함수를 지정해줘야 한다.
정렬 방법을 지정하는 함수는 다음과 같은 형식을 가지고 있어야 한다.
function compare(a, b) {
if (a가 어떠한 기준에 의해서 b보다 작을 경우)
return -1;
if (a가 어떠한 기준에 의해서 b보다 클 경우)
return 1;
//둘이 같을 경우
return 0;
}
꼭 -1와 1 값이 아니어도 되고 양수나 음수의 값을 반환해도 된다.
EX: 여러 정렬 함수를 이용한 배열 정렬.
// sort numbers asc
function compareNumbers(a, b) {
return a - b;
}
const numbers = [4, 2, 1, 5, 3];
numbers.sort(compareNumbers);
console.log(numbers); // [1, 2, 3, 4, 5]
// sort numbers desc
function compareNumbersDesc(a, b){
return b - a;
}
numbers.sort(compareNumbersDesc);
console.log(numbers); // [5, 4, 3, 2, 1]
// sort people by name
function sortByName (a, b) {
if (a.name < b.name)
return -1;
if (a.name > b.name)
return 1;
return 0;
}
const people = [
{ name: 'Peter', age: 16},
{ name: 'Mark', age: 18},
{ name: 'John', age: 27},
{ name: 'Jane', age: 14},
{name: 'Ester', age: 24}
];
people.sort(sortByName);
console.log(people);
// [{name: 'Ester', age: 24},
// {name: 'Jane', age: 14},
// {name: 'John', age: 27},
// {name: 'Mark', age: 18},
// {name: 'Peter', age: 16}]
map ( )
배열의 각 원소별로 지정된 함수를 실행한 결과로 새로운 배열을 반환한다.
forEach와 달리, 함수를 실행한 원소의 결과를 새로운 배열에 저장해서 반환한다.
EX: 배열의 각 원소를 2와 곱한다
const numbers = [2, 5, 7, 4, 3];
// before
let numbersDouble = [];
for (let i = 0; i < numbers.length; i++) {
numbersDouble.push(numbers[i] * 2);
}
// after
const numbersDouble = numbers.map((number) => number * 2);
console.log(numbersDouble); // [4, 10, 14, 8, 6]
EX: 사람들의 성과 이름을 하나의 문자열로 합친다
const people = [
{first: "Ester", last: "Kim"},
{first: "Hans", last: "Yoo"},
{first: "Celia", last: "Lee"},
{first: "Ben", last: "Whittaker"},
{first: "Harry", last: "Hart"}
]
function getFullName (person) {
return person.first + " " + person.last;
}
const fullNames = people.map(getFullName);
console.log(fullNames);
//["Ester Kim", "Hans Yoo", "Celia Lee", "Ben Whittaker", "Harry Hart"]
reduce ( )
배열의 각 원소별로 지정된 함수를 실행해서 누적된 하나의 결과를 반환한다.
map()와 달리, 배열이 아닌, 하나의 값을 반환한다.
함수 reduce는 두 인자(실행할 함수와 초기 값)를 받는다. 실행할 함수는 필수적으로 지정해야 하지만 초기 값은 선택이다. 초기 값이 없는 경우 배열의 첫 원소가 초기 값이 된다.
EX: 배열의 원소들의 총 합을 구한다
const numbers = [2, 5, 7, 4, 3];
// before
let total = 0;
for (let i = 0; i < numbers.length; i++) {
total += numbers[i];
}
// after
const total = numbers.reduce((accumulator, currentValue) => accumulator + currentValue);
console.log(total); // 21
EX: 3번 째 원소를 제외한 총합을 구한다(인덱스를 사용해야 하는 상황)
const numbers = [2, 5, 7, 4, 3];
// before
let total = 0;
for (let i = 0; i < numbers.length; i++) {
if (i == 2)
continue;
total += numbers[i];
}
// after
function addExcept3 (accumulator, currentValue, index) {
if (index == 3)
return accumulator;
return accumulator + currentValue;
}
const total = numbers.reduce(addExcept3);
console.log(total); // 17
지정한 함수에 세 번째 인자로 인덱스에 접근할 수 있다. reduce()를 사용할 때 명시할 점은 매 iteration마다 값을 반환해야지 누적된 값을 유지할 수 있다는 것이다.
아래처럼 index가 3이 아닐 경우에만 값을 반환하면 원하는 값을 얻지 못한다.
const numbers = [2, 5, 7, 4, 3];
function addExcept3 (accumulator, currentValue, index) {
if (index != 3)
return accumulator + currentValue;
}
const total = numbers.reduce(addExcept3);
console.log(total); // NaN
EX: 사람들의 나이의 합을 구한다 (초기 값을 지정해야 하는 상황)
const people = [
{ name: 'Peter', age: 16},
{ name: 'Mark', age: 18},
{ name: 'John', age: 27},
{ name: 'Jane', age: 14},
{name: 'Ester', age: 24}
];
const totalAge = people.reduce((accumulator, currentAge) => accumulator + currentAge.age, 0);
console.log(totalAge); // 99
만약 위의 예제 코드처럼 초기 값이 0을 지정해주지 않는다면 배열의 첫 원소가 초기 값이 된다. 따라서 첫 accumulator의 값은 객체가 되고 이후에 나이를 추가하면 문자열로 반환되어서 추가된다.
const people = [
{ name: 'Peter', age: 16},
{ name: 'Mark', age: 18},
{ name: 'John', age: 27},
{ name: 'Jane', age: 14},
{name: 'Ester', age: 24}
];
const totalAge = people.reduce((accumulator, currentAge) => accumulator + currentAge.age);
console.log(totalAge); // [object Object]18271424
요약
References
developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
yuddomack.tistory.com/entry/%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-Array-forEach
velog.io/@rememberme_jhk/JS-%EA%B3%A0%EC%B0%A8%ED%95%A8%EC%88%98-higher-order-function
'Programming Language > JavaScript' 카테고리의 다른 글
[번역] 최신 모드, "use strict" (0) | 2021.12.10 |
---|---|
자바스크립트에서 자주 사용하는 고차 함수(forEach, filter, sort, map, reduce) - 1 (0) | 2021.03.12 |
JavaScript란 이름은 어떻게 지어졌을까? (0) | 2021.03.09 |