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
에 대해서는 다음에 따로 정리 할 예정이다.