개발/Javascript

Iterator / Generator / Iterable

치돈포에버 2022. 6. 6. 19:02

*복습 자료라서 뻔한 내용은 생략

 

 

Summary.

 

 

1. Iterator는 Iterator Protocol을 준수하는 Object

    + Iterator Protocol은 next() 함수를 구현해두면 준수한 것

    + next() 함수는 value와 done을 포함한 Object를 반환 (value는 다음 값, done은 마지막 값이 consume 됐는지 여부)

    + Arrays must be allocated in their entirety, but iterators are consumed only as necessary

function makeRangeIterator(start = 0, end = Infinity, step = 1) {
    let nextIndex = start;
    let iterationCount = 0;

    const rangeIterator = {
       next() {
           let result;
           if (nextIndex < end) {
               result = { value: nextIndex, done: false }
               nextIndex += step;
               iterationCount++;
               return result;
           }
           return { value: iterationCount, done: true }
       }
    };
    return rangeIterator;
}
const it = makeRangeIterator(1, 10, 2);

let result = it.next();
while (!result.done) {
 console.log(result.value); // 1 3 5 7 9
 result = it.next();
}

 

2. Generator는 Iterator와 다르게 internal state 관리가 불필요함. function* 키워드를 사용함

    + Generator는 처음 실행 시 반환되고, 그 이후 next 호출 시마다 yield 까지 실행됨

    + Generator는 한번만 iteration 됨

    + 위 Iterator를 Generator로 구현하면 아래와 같음

    + next() 함수에 인자를 전달해서 Generator에 값 전달도 가능 (아래 참고)

    + return으로 최종 종료값 전달도 가능

    + https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_generators#advanced_generators

function* makeRangeIterator(start = 0, end = 100, step = 1) {
    let iterationCount = 0;
    for (let i = start; i < end; i += step) {
        iterationCount++;
        yield i;
    }
    return iterationCount;
}
function* fibonacci() {
  let current = 0;
  let next = 1;
  while (true) {
    let reset = yield current;
    [current, next] = [next, next + current];
    if (reset) {
        current = 0;
        next = 1;
    }
  }
}

const sequence = fibonacci();
console.log(sequence.next().value);     // 0
console.log(sequence.next().value);     // 1
console.log(sequence.next().value);     // 1
console.log(sequence.next().value);     // 2
console.log(sequence.next().value);     // 3
console.log(sequence.next().value);     // 5
console.log(sequence.next().value);     // 8
console.log(sequence.next(true).value); // 0
console.log(sequence.next().value);     // 1
console.log(sequence.next().value);     // 1
console.log(sequence.next().value);     // 2

 

3. In order to be iterable, an object must implement the @@iterator method. This means that the object (or one of the objects up its prototype chain) must have a property with a Symbol.iterator key.

    + spread 문법으로 array로 변환 가능

    + String, Array, Set, 등

function* makeIterator() {
    yield 1;
    yield 2;
}

const it = makeIterator();

for (const itItem of it) {
    console.log(itItem);
}

console.log(it[Symbol.iterator]() === it) // true;

// This example show us generator(iterator) is iterable object,
// which has the @@iterator method return the it (itself),
// and consequently, the it object can iterate only _once_.

// If we change it's @@iterator method to a function/generator
// which returns a new iterator/generator object, (it)
// can iterate many times

it[Symbol.iterator] = function* () {
  yield 2;
  yield 1;
};
const myIterable = {
    *[Symbol.iterator]() {
        yield 1;
        yield 2;
        yield 3;
    }
}

for (let value of myIterable) {
    console.log(value);
}
// 1
// 2
// 3

or

[...myIterable]; // [1, 2, 3]

 

4. Iterables 사용법

for (let value of ['a', 'b', 'c']) {
    console.log(value);
}
// "a"
// "b"
// "c"

[...'abc'];
// ["a", "b", "c"]

function* gen() {
  yield* ['a', 'b', 'c'];
}

gen().next();
// { value: "a", done: false }

[a, b, c] = new Set(['a', 'b', 'c']);
a;
// "a"

 

Additional.

1. (다음에) throw() 적절히 처리 안되면 종료됨. 처리될 경우 yield와 비슷한 동작?

 

 

Reference.