ES6부터 도입된Iterable,Iterator개념은 Javascript를 사용한다면 꼭 알아야할 개념이 되었다.
나도 단순히 반복할 수 있는 객체에 정의되어있는 규약이라고만 알고 있었는데 Array, Map, Set 뿐만 아니라 String도Iterable이라고 한다. 이번 기회에 제대로 이해하기 위해 정리하게 되었다.
Iterable protocol은
for..of구조와 같이 어떠한 value들이 loop 되는 것과 같은 iteration 동작을 정의하거나 사용자 정의하는 것을 허용하는 것을 말한다.
String,Array,Map, Set과 같은 특정한 타입들은 기본적으로 iteration 동작이 가능한 built-in iterables이다.
Iterable하기 위해선 @@Iterator 메소드를 구현해야하는데 이 말인 즉슨 해당 타입이 Symbol.iterator key의 속성을 가져야한다는 의미다.
[Symbol.iterator]: object를 반환하는, args가 없는 function
Iterator protocol은
next() 메소드를 가지고 있고value: iterator로부터 반환되는 모든 자바스크립트 값이며, done이 true일 경우 생략될 수 있다. done(boolean): iterator가 마지막 반복 작업을 마쳤을 경우 true, return 값이 아직 남아있다면 false를 반환한다. Iterable protocol을 통해 사용자만의 Iterator을 만들어 원하는 반복 행위를 설정할 수 있다. 이 때 위에 적혀진 iterator protocol의 정의를 만족하여야 한다.
let iterable = {
[Symbol.iterator]() {
let i = 5;
return {
next() {
return i === 0 ? {done: true} : { value: i--, done: false}
},
[Symbol.iterator]() { return this }
}
}
}
let iterator = iterable[Symbol.iterator]();
console.log(iterator)
console.log(iterable)
console.log(iterator.next())
console.log(iterator.next())
console.log(iterator.next())
/*
{
next: [Function: next],
[Symbol(Symbol.iterator)]: [Function: [Symbol.iterator]]
}
{ [Symbol(Symbol.iterator)]: [Function: [Symbol.iterator]] }
{ value: 5, done: false }
{ value: 4, done: false }
{ value: 3, done: false }
*/
// 남은 iterator value return
for (let a of iterator) console.log(a)
/*
2
1
*/
만약 Iterable의 iterator 메소드가 iterator 객첼르 반환하지 않는다면 그것은 잘 정의되지 못한 iterable이라고 할 수 있다. 이러한 iterable을 사용하는 것은 런타임 예외나 예상치 못한 결과를 불러올 수 있다고 한다. WellFormed Iterable의 특징은 [Symbol.iterator] 메소드가 실행된 결과 값이 자기 자신과 동일하다.
// Non-WellFormed
const nonWellFormedIterable = {
[Symbol.iterator]() {
let i = 2;
return {
next() {
return i === 0 ? {done: false} : {value: i--, done: false }
},
[Symbol.iterator]() {
return 1
}
}
}
}
const iter = nonWellFormedIterable[Symbol.iterator]();
console.log(iter.next())
console.log(iter.next())
console.log(iter.next())
console.log(iter)
console.log(iter.next())
console.log(iter[Symbol.iterator]() === iter)
/*
{ value: 2, done: false }
{ value: 1, done: false }
{ done: false }
{
next: [Function: next],
[Symbol(Symbol.iterator)]: [Function: [Symbol.iterator]]
}
{ done: false }
false
*/
// WellFormedIterable
const wellFormedIterable = {
[Symbol.iterator]() {
let i = 2;
return {
next() {
return i === 0 ? {done: true} : {value: i--, done: false }
},
[Symbol.iterator]() {
return this
}
}
}
}
const iter = wellFormedIterable[Symbol.iterator]();
console.log(iter.next())
console.log(iter.next())
console.log(iter.next())
console.log(iter)
console.log(iter.next())
console.log(iter[Symbol.iterator]() === iter)
/*
{ value: 2, done: false }
{ value: 1, done: false }
{ done: true }
{
next: [Function: next],
[Symbol(Symbol.iterator)]: [Function: [Symbol.iterator]]
}
{ done: true }
true
*/
// Array Iterator
let arr = [1, 2, 3]
let iter2 = arr[Symbol.iterator]()
console.log(iter2 === iter2[Symbol.iterator]())
// true
for...of문spread operator (전개문법)destructuring assignment (구조분해할당)yield*사용자 정의 Iterable은 generator(생성자)와 함께 많이 사용되는데, generator에 대해서는 다음에 따로 정리 할 예정이다.