프로그래밍 언어/JavaScript

JavaScript | 스프레드 연산자, forEach, map 기본 개념

hyuga_ 2024. 1. 9. 11:24

만일 다음과 같은 코드가 있다면,

const str = 'abcde';
[...str].map((e)=>console.log(e));
[...str].forEach((e)=>console.log(e));

 

map을 이용한 출력문과 forEach를 이용한 출력문은 각각 같은 결과를 낼 것이다. 

a
b
c
d
e

 

 

여기에 쓰인 스프레드 연산자, map 함수, forEach 함수는 자바스크립트 개발에서 아주 유용하게 쓰이는 것들이다. 

 

 

스프레드 연산자

[... OO]에 대하여

  • [... OO] 구문에서 '...'에 해당하는 부분은 자바스크립트에서 '스프레드(spread) 연산자'라고 불린다. 이 연산자는 이터러블(iterable) 객체의 개별 요소를 분리하여, 각 개별 요소들을 새로운 배열에 담아 반환한다. 
  • 위 예제에서는 str이라는 문자열 내의 각 문자를 개별 요소로 분리하여 배열에 넣었다. 예를 들어, str이 "apple"이었다면, [...str]는 ['a', 'p', 'p', 'l', 'e'] 이라는 새 배열을 생성할 것이다. 
  • 문자열은 이터러블 객체이므로, 스프레드 연산자를 사용하여 각 문자를 배열의 요소로 변환할 수 있다. 

이터러블(iterable) 객체? 

iterable은 '반복 가능'하다는 뜻이다. 자바스크립트에서는 주로 for .. of 루프로 내부 요소를 순회할 수 있는 객체를 모두 이터러블 객체라고 부른다. 정확히 얘기하면 Symbol.iterator 메서드를 갖고있는 객체들이다.

 

이 Symbole.iterator 메서드는 이터레이터(iterator) 객체를 반환하며, 이는 next() 메서드를 가진다. 이 next() 메서드는 { value, done } 형태의 객체를 반환하는데, value는 다음값이고, done은 반복이 끝났는지 여부를 나타낸다. 연결리스트 구현과 비슷하다. 스프레드 연산자는 해당 메서드를 호출하여 이터레이터를 가져오며, next() 메서드를 반복 호출하여 객체의 각 요소에 접근한다. 

 

이터러블 객체의 예시는 다음과 같다. 모두 각 내부 요소를 순차적으로 접근할 수 있는 객체들이다. 

  • 배열: 가장 일반적인 이터러블 객체이다. 
  • 문자열: 문자열도 각 문자에 순차적으로 접근할 수 있다. 
  • Map, Set 컬렉션
  • NodeList: DOM에서 반환되는 NodeList 객체들도 이터러블 객체이다.

 

 

스프레드 연산자는 다음과 같이 함수의 인자로 넘겨줄 때도 사용할 수 있다.

function sum(x, y, z) {
  return x + y + z;
}

const numbers = [1, 2, 3];
console.log(sum(...numbers));
// Expected output: 6


const chars = ['a', 'b', 'c'];
console.log(sum(...chars));
// Expected output: abc

 

 

 

 

forEach와 map 함수

이 메서드들의 풀네임(?)은 Array.prototype.forEach와 Array.prototype.map 이다. 

이 둘은 모두 배열의 각 요소에 대해 함수를 실행시키는 메서드이다. 

위의 예시 정도의 간단한 상황에서는 같은 결과를 가져오지만, 두 메서드는 사용 목적과 반환 값이 다르다. 

 

forEach

  • 이 메소드는 배열의 각 요소에 대해 주어진 함수를 실행하지만, 아무것도 반환하지 않는다. (undefined 반환)
  • 주로 배열의 각 요소에 대해 가벼운 처리를 할 때 사용된다. (출력, 로깅 등)

 

map

  • map 메소드는 배열의 각 요소에 대해 주어진 함수를 실행하고, 그 결과로 새로운 배열을 반환한다. 
  • 주로 배열의 각 요소를 변형하거나 새로운 형태의 데이터를 생성할 때 사용된다. 
  • 예를 들어, [1, 2, 3].map(x => x * x)는 [1, 4, 9]라는 새 배열을 반환한다.

 

간단히 정리하면, return이 필요하면 map, return 값이 필요 없으면 forEach 를 쓰면 된다. 

 

 

내부는 어떻게 생겨먹었을까?

동작 구조를 이해하는데 있어서, 내부 코드를 살펴보는 것만한게 없음을 경험적으로 알고있다. 

Array.prototype.forEach와 Array.prototype.map 메서드의 내부 구현은 자바스크립트 엔진에 따라 다르지만, 대략적으로 동작을 모방하기 위해서는 다음 정도의 코드면 충분하다. (실제 V8 엔진 등에서는 성능 최적화나, 예외 처리 등을 포함하여 더 복잡한 형태를 띄고있다.)

 

forEach

fake_ForEach = function(callback) {
    for (let i = 0; i < this.length; i++) {
        callback(this[i], i, this); // callback(각 요소, 해당 요소의 index, 배열 자체)
    }
}

 

 

map

fake_Map = function(callback) {
    let result = [];
    for (let i = 0; i < this.length; i++) {
        result.push(callback(this[i], i, this));
    }
    return result;
}

 

 

처음에 봤던 가장 간단한 예시를 대입해서 생각해보자. 

const str = 'abcde';
[...str].map((e)=>console.log(e));
[...str].forEach((e)=>console.log(e));