Iterator / Generator / Iterable
*복습 자료라서 뻔한 내용은 생략
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으로 최종 종료값 전달도 가능
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와 비슷한 동작?