[Symbol.iterator]()
메서드를 가진 값입니다.next()
메서드를 가진 값입니다.이터러블를 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)
위 코드의 arr
은 Symbol.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]