자, 일단 이터레이터라는 용어는 이제 익숙 할 것이다. 별 내용은 아니니, 잠깐의 아래의 예제를 보자.
const iterable = {
[Symbol.iterator](){
let i = 3;
return{
next () {
return i==0 ? {done:true}:{value: i--, done:false}
}
}
}
}
console.log([...iterable])
간단한 코드이다. iterable 객체는 [Symbol.iterator]을 제공 하고 있으니 iterable을 만족한다. 리턴 하는 iterator객체는 next함수를 제공하고 있고, value와 done을 제공하고 있으닌 Iterator를 만족한다.
자 그러면 잘만들어지 iterator? 그건 뭘까? 일단 제너레이터 펑션으로 만들면 잘만들어진 이터레이터일 것이다.
function* foo() {
yield 1
yield 2
yield 3
}
console.log([...foo()])
자, 아직까지는 솔직히 코드만 보고는 차이를 모르겠다. 하지만 이러면 어떤가?
const iterable = {
[Symbol.iterator](){
let i = 3;
return{
next () {
return i==0 ? {done:true}:{value: i--, done:false}
}
}
}
}
function* foo() {
yield 1
yield 2
yield 3
}
console.log([...foo()[Symbol.iterator]()]) // [1, 2, 3]
console.log([...iterable[Symbol.iterator]()]) // iterable[Symbol.iterator] is not ...
자 이게 무슨말을 하는 것일까? iterable[Symbol.iterator]() 로 만들어진 iterator는 iterable 하지 않고, foo()[Symbol.iterator]()로 만들어진 iterator는 iterable 하기도 하다는 뜻이 되게된다.
자 간단하게, generator 함수를 사용 하지 않고, Well-formed iterator를 만드는 패턴이다.
[Symbol.iterator] 함수가 this를 반환하는 것이다.
const wellformedIterable = function(i) {
return {
next () {
return i==0 ? {done:true}:{value: i--, done:false}
},
[Symbol.iterator]() { return this; }
}
}
function* foo(i) {
while(i) yield i--
}
console.log([...wellformedIterable(3)]) // [3, 2, 1]
console.log([...foo(3)]) // [3, 2, 1]
const a1 = wellformedIterable(3)
a1.next()
console.log([...a1]) // [2, 1]
const a2 = foo(3)
a2.next()
console.log([...a2]) // [2, 1]
이렇게 되면 이터레이터여도 for of 나 […] 등으로 남은 값을 소비할 수 있게 예를 들면 처음값을 제외하고, 무언가의 행위를 한다거나, 위와 같이 처음값이 아니여도, 필요한 만큼 사용하고, 나머지에 대한 평가를 위한 무언가를 할 수 있는 여지가 있게된다.
댓글
댓글 쓰기