이터러블(iterable)과 이터레이터(iterator)

김준엽·2022년 6월 17일
0

JavaScript

목록 보기
3/5

정의

  • 이터러블 : 이터레이터를 리턴하는 [Symbol.iterator]() 메서드를 가진 값입니다.
  • 이터레이터 : { value, done } 객체를 리턴하는 next() 메서드를 가진 값입니다.

자바스크립트에 내장된 이터러블 객체

  • Array, Set, Map, 문자열
  • map.keys(), map.values(), map.entries()

이터러블/이터레이터 프로토콜

이터러블를 for ...of, 전개 연산자 등과 함께 동작하도록 한 규약입니다.

const arr = [1, 2, 3];
for(const a of arr) console.log(a); /1, 2, 3

위 코드는 이터러블/이터레이터 프로토콜에 따라 Array가 for ...of를 통해 순회하고 있습니다.

const arr = [1, 2, 3];
arr[Symbol.iterator] = null;
for(const a of arr) console.log(a); //에러 발생(arr is not iterable)

위 코드의 arrSymbol.iterator가 없어서 이터러블/이터레이터 프로토콜을 따르지 못합니다. for ...of는 Symbol.iterator가 값이 있어야만 순회할 수 있습니다. 그리고 for ...of는 이터레이터의 done: true일 때 순회가 종료된다.

const arr = [1, 2, 3];
const iter = arr[Symbol.iterator](); //iterator이자 iterable
console.log(iter.next()); //{value: 1, done: false}
for(const a of iter) console.log(a); //2, 3

console.log(iter[Symbol.iterator]() == iter) // true

[Symbol.iterator]()로 이터레이터를 반환해서 next()호출 후 for ...of로 순회했습니다. 여기서 왜 이터레이터인 iter은 for ...of로 순회가능했을까요? iter에도 Symbol.iterator가 존재하기 때문입니다. iter[Symbol.iterator]()는 자기 자신을 반환합니다.

그래서 iter은 이터레이터이자 이터러블입니다. 이걸 well-formed iterator/iterable이라 한다. 자바스크립트 내장 객체 Array, Set, Map 등은 여기에 해당합니다.


사용자 정의 이터러블

const iterable = {
    [Symbol.iterator]() {
        let i = 3;
        return {
            next() {
                return i === 0 ? { done: true } : { value: i--, done: false };
            },
        }
    }
};
for (const a of iterable) console.log(a); //3, 2, 1

사용자 정의 이터러블을 만들었습니다. for ...of를 문제없이 순회하고 있지만 위 코드는 문제가 있습니다.

const iter = iterable[Symbol.iterator]();
for (const a of iter) console.log(a); //error: iter is not iterable

이터레이터가 Symbol.iterator속성이 없기 때문에 순회하지 못합니다. 이건 well-formed iterator가 아닙니다.

그러면 well-formed iterator를 만들어 보겠습니다.

const iterable = {
    [Symbol.iterator]() {
        let i = 3;
        return {
            next() {
                return i === 0 ? { done: true } : { value: i--, done: false };
            },
	    [Symbol.iterator]() {
	        return this;
	    }
        }
    }
};
const iter = iterable[Symbol.iterator]();
for (const a of iter) console.log(a); //3, 2, 1
console.log(iter[Symbol.iterator]() == iter); //true

이터레이터가 [Symbol.iterator]()를 실행했을 때 자기 자신을 반환하면 well-formed iterator가 됩니다.


전개 연산자, 구조 분해, 나머지 연산자

전개 연산자, 구조 분해, 나머지 연산자로 이터러블을 다루어 보겠습니다.

const iterable = {
    [Symbol.iterator]() {
        let i = 3;
        return {
            next() {
                return i === 0 ? { done: true } : { value: i--, done: false };
            },
						[Symbol.iterator]() {
								return this;
						}
        }
    }
};

console.log(...iterable); // 3 2 1
console.log([...iterable, ...iterable]); [3, 2, 1, 3, 2, 1]

const [head, ...rest] = iterable;
console.log(head); //3
console.log(rest); //[2, 1]
profile
프론트엔드 개발자

0개의 댓글