[Javascript] 이터레이터(iterator)

박세화·2024년 1월 21일
0

Javascript

목록 보기
11/12

📌 이터레이터(Iterator)

반복 처리가 가능한 객체를 의미하며, next() 라는 메소드를 가진다.
이때 next(){value : 값 , done : true/false} 형태의 객체를 리턴한다.
[Symbol.iterator] 라는 key 값의 value로서 정의되어있다.

💡 예시로 만든 이터레이터 함수

const customIter = (() => {
    let current = 1;
    const max = 5;
    return { 
      next() {
        return { value: current++, done: current > max + 1 };
      }
    }
})();

1) customIter 함수를 실행시킨다.
2) next() 메소드를 반환하는데, 이 메소드는 done의 값이 true가 될 때까지 value 값을 업데이트하며 이터러블 객체를 만든다.
3) done=true 이면 반복이 종료되었다는 것을 말한다.


이터레이터 함수를 활용해 평범한 객체를 이터러블로 만들기

const obj = { name: 'o', id: 1 }

console.log([...obj]) // Type '{ name: string; id: number; }' must have a '[Symbol.iterator]()' method that returns an iterator.

const obj = {
    [Symbol.iterator]() {
        let cur = 1;
        const max = 5;
        return {
            next() {
                return { value: cur++, done: cur > max + 1 };
            },
        };
    },
    name: 'o',
    id: 1,
    6: 'test',
};

console.log([...obj]) // (5) [1, 2, 3, 4, 5]
console.log({ ...obj }) // {6: 'test', name: 'o', id: 1, Symbol(Symbol.iterator): ƒ}

해당 코드는 이터러블이 아닌 평범한 객체를 [Symbol.iterator]()을 사용해 이터러블로 만드는 코드이다.
[Symbol.iterator] 을 key로, 그리고 이터레이터 함수를 value 값으로 넣어준다.

여기서 리턴 부분인 return { next() {return { value: cur++, done: cur > max + 1 }; }, };이터레이터가 된다.
max 값인 5까지만 반복하여 1, 2, 3, 4, 5 로 구성된 이터러블 객체를 만들 것이다.

+)

콘솔로 객체를 츨력해보면 [Symbol.iterator]가 추가된 것을 확인할 수 있다!


💡 for of 구문으로 반복해보기

이제 obj 객체는 이터러블이기 때문에 for of 구문을 사용해서 반복할 수 있다. for in 문을 사용하면 객체의 key 값이 나오는데, for of 구문을 사용하면 이터레이터로 만든 1부터 5까지가 출력되는 이유가 무엇일까?

😮 for of 구문을 이터러블 객체에서만 사용할 수 있는 이유

➡️ for of 가 [Symbol.iterator]를 호출하여 이터레이터를 실행시키는 방식으로 동작하기 때문이다.

  • for of에 다음 값이 필요하면, for of는 이터레이터의 next()메서드를 호출한다.
  • next()의 반환 값은 {done: Boolean, value: any}와 같은 형태이어야 한다. 그래야 순회가 된다.
  • done=true는 반복이 종료되었음을 의미한다. done=false일땐 value에 다음 값이 저장된다. 이런 식으로 요소를 순회한다.

📌 이터레이터와 배열

이터레이터 자체가 배열을 생성하진 않는다

const iterable = {
    [Symbol.iterator]() {
        let cur = 1;
        const max = 5;
        return {
            next() {
                return { value: cur++, done: cur > max + 1 };
            },
        };
    },
};

const iterator = iterable[Symbol.iterator]();
let result = iterator.next();
result = iterator.next();

console.log(result) // {value: 2, done: false}

Array.isArray(iterable) // false
Array.isArray(Array.isArray([...iterable])) // true

iterable[Symbol.iterator] 을 key로, 그리고 이터레이터 함수를 value 값으로 가진다.

하지만 이터레이터 자체는 반복 후 배열을 생성하여 저장하진 않는다.
클로저를 통해 current 값을 참조하고 있을 뿐이다.

result 라는 변수에 iterator.next()를 할당하여 한 번 실행한 후 콘솔에 result를 출력해보면, {value: 2, done: false} 리턴값이 그대로 출력된다.

따라서 iterable 자체는 아직 배열이 아니며, 배열을 만들고 싶다면 스프레드 연산자를 이용해 배열로 만들어줘야 한다.

💡 스프레드 연산자[Symbol.iterator]라는 키가 있는지 확인하고 있다면 그 키에 해당하는 함수를 계속 실행한다. (value값 업데이트)
그리고 거기서 나온 데이터를 새로운 배열에 저장한다!

즉 이터레이터가 품고있는 함수에 따라 출력되는 값이 달라지고, 배열은 그 모든 값을 이터레이터를 순환시켜 저장하여 만들어지게 된다.

✅ 스프레드 연산자
ES6에 추가된 문법으로, Array, Map, Set, String 등의 원소를 나열하여 복사할 때 사용된다.
얇은 복사를 수행하기 때문에 객체의 참조값(메모리 주소값)을 동일하게 참조하는 방식으로 복사한다.
배열의 스프레드 연산자와 객체의 스프레드 연산자의 구동 방식이 다른데,

  • 배열에서 사용 ➡️ 이터러블을 사용해서 요소를 반복하고 숫자 순서를 유지하는데 중점을 두고 복사
  • 객체에서 사용 ➡️ Object.assign과 같이 반복하지 않고 그냥 얕은 복사 (객체의 키에는 순서가 없기 때문)

😮 이터레이터를 배열 대신 사용할 수도 있다

const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

이건 우리가 흔히 아는 배열이다.
배열의 자료구조상, 요소 10개에 해당하는 만큼의 메모리가 필요하다.

const makeArray = {
    [Symbol.iterator]() {
        let cur = 1;
        const max = 10;
        return {
           next() {
              return { value: cur++, done: cur > max + 1 };
           },
        };
    }
};
const makedArray = [...makeArray]
console.log(makedArray)  // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

만약 이터레이터를 활용해서 같은 배열을 만들고 싶다면 이런 코드를 작성하면 된다.
그런데 이때는 cur, max 두 변수에 해당하는 메모리만 사용하면 된다.
배열 자체를 만드는 것은 스프레드 연산자가 이터레이터 함수를 반복 순회할 때이다.

➡️ 다뤄야 하는 데이터의 양이 매우 많을 때는 배열보다 이터레이터가 훨씬 효율적일 수 있다!


출처
[JS] 📚 이터러블 & 이터레이터 - 💯완벽 이해
[JavaScript] 이터레이터(Iterator)와 배열(Array)의 차이점

0개의 댓글