[ES6] Iterable, Iterator에 대하여

주형(Jureamer)·2022년 11월 22일
0

ES6부터 도입된 Iterable, Iterator 개념은 Javascript를 사용한다면 꼭 알아야할 개념이 되었다.
나도 단순히 반복할 수 있는 객체에 정의되어있는 규약이라고만 알고 있었는데 Array, Map, Set 뿐만 아니라 String도 Iterable이라고 한다. 이번 기회에 제대로 이해하기 위해 정리하게 되었다.

Iterable & Iteration protocol이란?

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() 메소드를 가지고 있고
  • 아래 2개 속성들을 가진 object를 반환하고, arguments가 없는 function이다.
    • value: iterator로부터 반환되는 모든 자바스크립트 값이며, done이 true일 경우 생략될 수 있다.
    • done(boolean): iterator가 마지막 반복 작업을 마쳤을 경우 true, return 값이 아직 남아있다면 false를 반환한다.

사용자 Iterator 정의하기

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
*/

WellFormed & NonWellFormed Iterables

만약 Iterableiterator 메소드가 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

Iterable과 함께 사용되는 문법

  • for...of
  • spread operator (전개문법)
  • destructuring assignment (구조분해할당)
  • yield*

사용자 정의 Iterable은 generator(생성자)와 함께 많이 사용되는데, generator에 대해서는 다음에 따로 정리 할 예정이다.

참고

profile
작게라도 꾸준히 성장하는게 목표입니다.

0개의 댓글