들어가며

나는 평소 백엔드 API 로부터 응답받은 데이터를 가져와 화면에 보여줄 때 forEach 함수를 자주 사용하곤 했다. for 문에 비해 가독성이 좋아서 forEach 를 사용했었다. 이외에도 케이스에 따라 filter, map, reduce 등을 사용하곤 했었다. 하지만 이들의 퍼포먼스 차이에 대해서는 아직까지 찾아본 적이 없었다.

오늘 백준에서 이진 탐색 알고리즘 문제를 풀던 중 filter, reduce 메서드 등을 사용하게 되었다. 코드가 올바르게 동작해 예상한 값이 출력되었지만, 메모리 초과로 계속 통과하지 못했다. 그러나, 이들 대신 forEach 메서드를 사용하니 메모리 초과 문제가 해결되며 통과했다.

이를 계기로 filter, reduce 메서드와 forEach 메서드가 어떤 퍼포먼스 차이가 있는지 궁금하여 자세히 찾아보기로 결심했다.

비교

for loop

for (let i = 0; i < 10; i++) {
  console.log(arr[i]);
}
  • 가장 빠르고 단순하다.
  • 모든 자료형에 대해 사용 가능하다.
  • 중간에 loop 건너뛰기(continue)나 종료(break)가 가능하다.
  • 내가 원하는 반복 순서 및 반복 횟수를 설정할 수 있다.
    • 반복 간격 설정 예시 : i++, i—, i += 2*i
    • 반복 횟수 설정 예시 : i < 20, i > 0
  • 변수를 활용할 수 있다. (let i 값을 사용할 수 있다.)

forEach

arr.forEach(function(value, index, array) {
  console.log(value);
});
  • 일반 for문보다 훨씬 가독성이 좋다.
for (let i = 0; i < arr.length; i++) {
  console.log('element ', i, arr[i]);
  console.log(arr[i].property1 + arr[i].property[2]);
  console.log(arr[i].property2);
}

arr.forEach(function (v, i) {
  console.log('element ', i, v);
  console.log(v.property1 + v.property2);
  console.log(v.property2);
});
  • 같은 구문이라도 다음과 같이 가독성 면에서 차이가 있다.
  • forEach는 복잡한 객체를 처리하는 데 있어서 유리하다.
  • 하지만 forEach 문은 구문 밖으로 return 값을 받지 못한다.
  • const arr = [1, 2, 3, 4, 5]; const newArr = arr.forEach(function(e, i) { return e; }); // undefined
  • 빠른 편이다.
  • Array 객체에서 사용 가능하다.
  • for 문과 다르게 중간에 멈출 방법이 없다.
  • return 값을 받지 못한다.

filter

const newArr = arr.filter(function(v, i, arr) {
  return condition;
});
  • filter의 가장 큰 특징은 boolean 형태의 return 값을 받는다.
  • return 값이 true인 경우, 그 요소를 반환하고 false일 경우, 반환하지 않는다. 기본값은 false이다.
  • 깔끔하게 원하는 요소들만 필터링할 수 있는 유용한 메서드이다.
const arr = [0, , , 1, , , , , 2, , , , , 3];
const newArr = arr.filter(function() { return true });
console.log(newArr); // [0, 1, 2, 3]
const arrOfTruthy = arr.filter(function(el) { return el });
console.log(arrOfTruthy); // [1, 2, 3]
  • 빠른 편이다.
  • Array 객체에서 사용 가능하다.
  • chainable 하다.filter 메서드 호출부 바로 뒤에 다른 배열 메서드(forEach)를 호출할 수 있다.
  • const arr = [1,2,3,4,5,6,7,8,9,10]; const multipleOf2Arr = arr .filter(function(v, i) { return v % 2 == 0 }) .forEach(function(v, i) { console.log("element ", v); };
  • 빈 배열 요소를 반환하지 않는다.
  • const arr = [0, , , 1, , , , , 2, , , , , 3]; const newArr = arr.filter(function() { return true }); console.log(newArr); // [0, 1, 2, 3]
  • 대용량 배열 처리 시 메모리 overflow 가능성이 있다.
    • 메모리 overflow : 사용 가능한 메모리 영역을 넘어설 때 발생하는 오류. 즉, 메모리 할당 가능한 영역보다 더 많은 메모리 할당을 하면 발생하게 된다.
    • 메모리 overflow는 해킹 공격에 사용될 수 있다.
    • 이를 방지하기 위해 메모리의 한계를 검사하는 경계 검사를 철저히 해야 한다.
  • return 값은 true/false이며, 요소를 반환한다.

map

const newArr = arr.map(function(v, i, arr) {
  return condition;
});
  • map은 filter와 달리 요소가 아닌 새로운 값을 만들어서 return할 수 있다.
const arr = [1, 2, 3, 4, 5];
const newArr = arr.map(function(v, i, arr) {
  return v + 1;
});
console.log(newArr); // [2, 3, 4, 5, 6]
  • 빠른 편이다.
  • Array 객체에서 사용 가능하다.
  • chainable 하다.
  • 대용량 배열 처리 시 메모리 overflow 가능성이 있다.
  • return 값 자체를 반환한다.

reduce

const arr = [1, 2, 3, 4, 5];
const sumOfValue = arr.reduce(function(acc, v, i, arr) {
  return acc + v;
});
console.log(sumOfValue); // 15
  • 첫 번째 인자인 accumulator는 return 값을 누적한다.
  • 다음 루프에서 계속 accumulator를 전달받아 사용할 수 있다.
const arr = [1, 2, 3, 4, 5];
const sumWithInitVal = arr.reduce(function(acc, v, i, arr) {
  return acc + v;
}, 100);
console.log(newArr); // 115
  • 초기값 설정으로 인해 115 값이 나온 걸 확인할 수 있다.

이 내용을 공부하며 깨달은 것

  • 각 케이스에 맞춰 적합한 Array 메서드를 활용하자.
  • filter, map 메서드를 사용하여 대용량 배열 처리 시 메모리 overflow 가능성이 있으므로 주의하자.

참조

'프로그래밍 언어 > JavaScript' 카테고리의 다른 글

[JavaScript] 자바스크립트 언어의 특징  (0) 2022.11.05
[Tip] function 은 되도록 사용하지 마세요.  (2) 2022.11.04
Generator  (0) 2020.11.23
JavaScript와 Iterator  (0) 2020.11.23
Node와 Element  (0) 2020.11.22
복사했습니다!