Javascript는 간단한 for 루프에서 map() 함수와 filter()에 이르기까지, 컬렉션을 반복하는 방법이 여럿 존재합니다. 반복기및 생성기는 반복 개념을 핵심 언어 내로 가져와 for...of 루프의 동작을 사용자가 직접 정의하는 매커니즘을 제공합니다.
1. 반복자
반복자(lterator)는 시퀀스를 정의하고 종료시의 반환값을 잠재적으로 정의하는 객체입니다. 두 개의 속성 (value, done)을 반환하는 next() 메소드를 사용해 객체의 lterator protocol을 구현합니다. 시쿼스의 마지막 값이 이미 산출되었다면 done 값은 true가 됩니다. 만약 value 값이 done과 함께 존재하면, 그것은 반복자의 반환값이 됩니다.
반복자를 생성하면 next() 메소드를 반복적으로 호출해 명시적 반복을 시킬 수 있습니다. 반복자를 반복시키는 것은 일반적으로 한 번씩만 할 수 있어, 반복자를 소모시키는 것이라 할 수 있습니다. 마지막 값을 산출하고나서 next()를 추가적으로 호출하면 { done : next } 가 됩니다.
JavaScript에서 가장 일반적인 반복자는 배열 반복자로, 배열의 각 값을 순서대로 반환합니다. 모든 반복자가 배열로 표현될 수 있다고 생각하지만 배열은 완전히 할당되어야 하지만, 반복자는 필요한만큼만 소모되므로 무제한 시퀀스로 표현할 수 있습니다. 이를 테면 0부터 무한대 사이의 정수범위처럼..
function makeRangeIterator(start = 0, end = Infinity, step = 1) {
var nextIndex = start;
var n = 0;
var rangeIterator = {
next: function () {
var result;
if (nextIndex < end) {
result = { value: nextIndex, done: false };
} else if (nextIndex == end) {
result = { value: n, done: true };
} else {
result = { done: true };
}
nextIndex += step;
n++;
return result;
},
};
return rangeIterator;
}
위 반복자를 사용하면 아래와 같습니다.
var it = makeRangeIterator(1, 4);
var result = it.next();
while (!result.done) {
console.log(result.value); // 1 2 3
result = it.next();
}
console.log("Iterated over sequence of size: ", result.value);
/ *
1
2
3
Iterated over sequence of size: 3
*/
2. Generator functions
잘 만들어진 반복자(lterator)는 유용한 도구인 반면, 이것을 생성할 때는 주의해서 프로그래밍 해야 하며, 반복자 내부에 명시적으로 상태를 유지할 필요가 있는데, 생성자(Generator) 함수는 이에 강력한 대응을 제공합니다. 실행이 연속적이지 않은 하나의 함수를 작성해 개발자가 iterative algorithm을 정의할 수 있습니다.
생성자 함수는 function* 문법을 사용해 작성됩니다. 생성자 함수가 최초로 호출될 때, 함수 내부의 어떠한 코드도 실행되지 않고, 대신 생성자라고 불리는 반복자 타입을 반환합니다. 생성자의 next 메소드를 호출함으로써 어떤 값이 소비되면 생성자 함수는 yield 키워드를 만날때 까지 실행됩니다.
생성자 함수는 원하는 만큼 호출할 수 있으며, 매번 새로운 생성자를 반환합니다. 하지만 각 생성자는 단 한번만 순회할 수있습니다.
function* makeRangeIterator(start = 0, end = Infinity, step = 1) {
let s = 0;
for (let i = start; i < end; step) {
s++;
yield i;
}
return s;
}
3.Iterables
객체는 값이 for..of 구조 내 반복되는 것 같은 반복 동작을 정의하는 경우 반복이 가능합니다. Array 또는 Map과 같은 일부 내장 형은 기본 동작이 있으나 다른 형은(Object)은 없습니다. 반복 가능하기 위해, 객체는 @@iterator 메서드를 구현해야 합니다. 즉, 객체(혹은 그 프로토타입 체인에 등장하는 객체 중 하나)가 Symbol.iterator 키를 갖는 속성이 있어야 합니다.
하나의 iterable은 단 한 번, 혹은 여러번 반복 가능하며, 어떤 순간에 어떻게 사용할 것인가는 프로그래머의 몫입니다. 단 한번 반복 가능한 iterable(e.g Genrator)은 관습적으로 자신의 @@iterator 메소드로부터 this를 반환합니다. 반면, 여러 반복 가능한 iteables는 @@iterator 메소드가 호출되는 매회 새로운 iterator를 반드시 반환해야 합니다.
사용자 정의 iterable
var myItrable = {
*[Symbol.iterator]() {
yield 1;
yield 2;
yield 3;
},
};
for (let value of myItrable) {
console.log(value);
}
// 1 2 3
console.log([...myItrable]); // [1,2,3]
내장 iterable
String, Array, TypedArray, Map 및 Set은 모두 내장 반복가능 객체로, 그들의 프로토타입 객체가 모두 Symbol.iterator 메서드가 있습니다.
iterable을 기대하는 구문
일부 문(statement) 및 식(expression)은 iterable합니다, 가령 for-of 루프, spread syntax, yield* 및 해체 할당
for (let value of ["a", "b", "c"]) {
console.log(value);
}
// "a" , "b" "c"
// "a" , "b" "c"
console.log([..."abc"]);
function* gen() {
yield* ["a", "b", "c"];
}
console.log(gen().next()); // { value: "a" done: false}
[a, b, c] = new Set(["a", "b", "c"]);
a; // a
4.Generator 심화
생성자 함수는 요청에 따라 산출된 값을 계산하고, 계산하기 비싼(힘든) 수열 또는 위에 설명한 대로 무한 수열이라도 효율적으로 나타내게 합니다. next() 메서드는 또한 생성기의 내부 상태를 수정하는 데 쓰일 수 있는 값을 받습니다. next()에 전달되는 값은 생성기가 중단된 마지막 yield 식의 결과로 처리됩니다.
function* fibonacci() {
var fn1 = 0;
var fn2 = 1;
while (true) {
var current = fn1;
fn1 = fn2;
fn2 = current + fn1;
var reset = yield current;
if (reset) {
fn1 = 0;
fn2 = 1;
}
}
}
var 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
제너레이터의 throw() 메서드를 호출하고 throw해야 하는 예외 값을 전달하여 생성자가 예외를 throw 하도록 할 수 있습니다. 이 예외는 생성기의 현재 일시 중단된 컨텍스트에서 throw됩니다. 마치 현재 일시 중단된 yield가 대신 throwvalue 문인 것 처럼, 예외가 생성기 내 포함되지 않을 때 throw() 호출을 통해 전파되고 이후 next() 호출은 done 속성이 true가 됩니다.
제너레이터에는 주어진 값을 반환하고 제너레이터 자체를 완료하는 return(value)가 있습니다.
참고 자료
반복기 및 생성기 - JavaScript | MDN
컬렉션 내 각 항목 처리는 매우 흔한 연산입니다. JavaScript는 간단한 for 루프에서 map() 및 filter()에 이르기까지, 컬렉션을 반복하는 많은 방법을 제공합니다. 반복기(iterator) 및 생성기(generator)는
developer.mozilla.org
GitHub - javascript-only/javascript-bloging
Contribute to javascript-only/javascript-bloging development by creating an account on GitHub.
github.com
'Front-End > JavaScript' 카테고리의 다른 글
[Javascript] 14강 Javascript의 프로토타입 객체 (1) | 2025.02.15 |
---|---|
[Javascript] 13강 자바스크립트 모듈 (0) | 2025.02.13 |
[Javascript] 11강 형식화 배열 (1) | 2025.02.09 |
[Javascript] 10강 Promise (0) | 2025.02.08 |
[Javascript] 9강 key 기반 컬렉션, 객체로 작업하기 (0) | 2025.02.08 |