코딩테스트 문제를 풀다가 간단한 코드인데 for문으로 하면 통과인데 forEach문으로 하면 실패하는 문제가 있었다.
for문이 더 빠르다는 것은 알고있었지만 어느정도 차이나는지 forEach가 무엇때문에 속도가 느린지가 궁금했다.
또한 for문과 forEach의 차이점이 내가 알고있는 것이 전부일까? 더 많은 차이점이 있지않을까 궁금했다.
다른사람들이 말하는 것 처럼 둘의 차이가 어느정도인지 궁금해졌다.
먼저 간단한 개념먼저 알고가자
1. 개념
1) for
for문은 ES1 부터 있던 문법이다.
for ([initialization]; [condition]; [final-expression]) {
statement
}
아래 세 가지 모두 생략이 가능하다.
- initialization : var 또는 let 키워드를 사용해 변수 선언가능 (var로 선언하면 전역, let 키워드로 선언한 변수는 반목문의 지역 변수가 된다)
- condition : 매 반복마다 평가할 식 (이 식을 넣지 않알을 때 계산 결과는 언제나 참, 계산 결과가 거짓이라면 for 문의 바로 다음 식으로 건너 뛴다. 다만 식을 넣지 않는다면 무한 루프에 빠질 수 있기 때문에 탈출할 수 있는 장치를 넣어야 한다)
- final-expression : 매 반복 후 평가할 식, 다음번 condition 평가 이전에 발생한다.
let str = '';
for (let i = 0; i < 9; i++) {
str = str + i;
}
console.log(str);
// Expected output: "012345678"
var i = 0;
for (;;) {
if (i > 3) break;
console.log(i);
i++;
}
2) forEach
forEach는 ES5에 추가된 문법이다.
forEach의 핵심은 주어진 callback을 배열에 있는 각 요소에 대해 오름차순으로 한 번씩 실행한다는 것이다.
또한 forEach()로 처리할 요소의 범위는 최초 callback 호출전에 설정된다. 즉 호출을 시작한 뒤 배열에 추가한 요소는 callback이 방문하지 않는다.
arr.forEach(callback(currentvalue[, index[, array]])[, thisArg])
const array1 = ['a', 'b', 'c'];
array1.forEach(element => console.log(element));
// Expected output: "a"
// Expected output: "b"
// Expected output: "c"
2. 공통점
1) 스코프
for문에서 var말고 let으로 선언한 변수와 forEach 메서드내의 콜백 함수에 선언된 매개변수는 모두 지역 변수된다.
📌 for문
const num = 4;
const arr = [0, 1, 2];
for (let num = 0; num < arr.length; num++) {
console.log(num);
}
//Result:
//0
//1
//2
console.log(num);
//Result
//4
📌 forEach
const num =4;
const arr = [0,1,2];
arr.forEach(num =>{
console.log(num);
});
//Result:
//0
//1
//2
console.log(num);
//Result
//4
3. 차이점
1) 속도
for 문이 forEach보다 더 빠르다 하지만 자료를 찾아보니 브라우저 환경에따라 속도가 달라질 수 있다는 것도 알았다.
매우 큰 배열이나 반복 횟수가 많은 상황에서는 for문보다 forEach가 함수 호출 오버헤드가 발생할 수 있기때문에 조금 더 속도 차이가 날 수 있다.
2) 가독성
아래 코드만 보아도 for문보다 forEach문이 훨씬 깔끔해서 가독성이 좋다.
for 문
for(let i =0; i<foodArray.length; i++){
let food = foodArray[i];
console.log(food);
for(let j=0; j<food.ingredients.length; j++){
let ingredient = food.ingredients[j];
console.log(ingredient);
}
}
forEach
foodArray.forEach((food)=>{
console.log(food);
food.ingredients.forEach((ingredient)=>{
console.log(ingredient);
});
});
3) 오류 가능성
(1) for문
일반적인 경우 for문이 설정해줘야하는 조건이 더 많고 복잡하기에 오류가 발생할 가능성이 더 높다 예를 들어
for(let i =0; i<=foodArray.length; i++){
let food = foodArray[i];
console.log(food);
for(let j=0; j<food.ingredients.length; j++){
let ingredient = food.ingredients[j];
console.log(ingredient);
}
}
위 코드는 기존 i<foodArray.length 에서 "=" 하나만 추가되었는데 바로 에러가 발생한다.
이런식으로 작은 실수가 에러를 불러올 수 있다.
하지만 forEach에서는 이러한 실수를 아예 하지 않을 수 있다.
(2) forEach
forEach는 동기적으로 동작한다. 하지만 경우에따라 마치 비동기처럼 보일 수도 있다. (forEach는 비동기가 아니다)
그 이유는 forEach는 배열을 순차적으로 돌며 callback을 실행할 뿐이지 그 callback이 동기인지 비동기인지는 상관하지 않는다.
즉 callback이 끝날때까지 기다려주지 않고 그냥 순차적으로 배열을 순회하면 callback을 호출할 뿐이다.
예를들어보자
let newQuizIds = [];
quizs.forEach(async quiz => {
const tmp = await new quizModel(quiz).save();
newQuizIds.push(tmp._id);
});
console.log(newQuizIds);
이와 같은 코드를 작성하면 console에는 빈 배열만 나올 것이다.
왜냐하면 forEach는 콜백을 호출만 할 뿐 기다리지 않기 때문에 호출이 다 끝나면 바로 console.log를 실행시켜서
newQuizIds에 담기기전에 출력이 되는 것이다.
따라서 forEach를 사용할 때는 반드시 이점을 알고 코드를 작성해야 한다.
이러한 차이점 때문에 비동기 코드를 넣었을 때 다른 결과를 초래한다.
for문에서는 순차적으로 진행되기에 만약 비동기코드 처리중에 오류가 발생하면 코드가 멈춰서 다음 라인의 코드가 실행되지 않지만
forEach에서 비동기를 사용한다면 callback이 처리되는 것을 기다리지 않고 호출만 전부 한뒤 다음 라인의 코드가 실행되기에
다음라인의 코드가 실행되고 오류가 발생할 가능성이 있다.
생각해볼 수 있는 또 다른 예
const arr = [1, 2, 3];
arr.forEach(async (item) => {
const response = await fetch(` https://api.example.com/data/$ {item}`);
const data = await response.json();
console.log(data);
});
console.log('Done');
✅ 해결법
콜백 호출을 순차적으로 실행하는 forEach 대신 코드 자체를 순차적으로 실행하는 for 이나 for of를 사용하면 된다.
let newQuizIds = [];
for (let quiz of quizs) {
const tmp = await new quizModel(quiz).save();
newQuizIds.push(tmp._id);
}
console.log(newQuizIds);
4) for 문을 사용하면 원하는 부분에서 for loop에서 벗어날 수 있다.
for 문을 사용하면 break 를 사용해서 원하는 부분에서 for문을 중단시킬 수 있다.
continue를 사용해 해당 부분을 스킵할 수도 있다.
하지만 forEach는 예외를 던지지않고는 멈추지 못한다.
다만 continue처럼 return을 활용해서 스킵은 가능하다.
const arr = [1,2,3,4,5];
arr.forEach((value) => {
if(value == 3) return;
console.log(value);
});
[참고]
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
https://develogger.kro.kr/blog/LKHcoding/66
'Javascript > 공부' 카테고리의 다른 글
CSS-in-JS (0) | 2023.07.23 |
---|---|
[공부] 계산기 만들기 <이전자료> (0) | 2022.10.30 |