[JavaScript]제너레이터

LMH·2022년 12월 25일
0
post-thumbnail

이번 포스팅에는 제너레이터에 대해서 정리하겠습니다.

제너레이터란?

일반적으로 자바스크립트에서 함수는 리턴값 가지지 않거나 1개를 가지는게 원칙입니다. 그러나 제너레이터(generator)를 사용하면 여러 개의 값을 필요에 따라 하나씩 반환(yield)할 수 있습니다.

제너레이터 함수는 일반 함수와 다르게 호출 시 실행되지 않고 제너레이터 객체를 생성해 반환합니다. 제너레이터 함수가 반환한 제너레이터 객체는 이터러블이면서 동시에 이터레이터 입니다.

즉, 제너레이터는 symbol.iterato 메소드를 상속받는 이터러블이면서 value, done 프로퍼티를 갖는 이터레이터 리절트 객체를 반환하는 next 메소드를 가지는 이터레이터 입니다.

generate 내부의 로직을 실행시키기 위해서는 제너레이터 객체의 next 메소드를 사용해야합니다.

function* generate() {
	yield 5;
    yield 10;
    return 15;
}

const generator = generate();
getnerator.next(); // {value: 5, done: false}
getnerator.next(); // {value: 10, done: false}
getnerator.next(); // {value: 5, done: ture}
getnerator.next(); // {value: undefined, done: true}

위의 코드를 보면 next 메소드를 처음 실행하면 value, done 프로퍼티를 가집 객체를 리턴합니다. value는 yield 키워드를 통해 산출되는 값이며 done은 함수 코드 실행이 종료 입니다. done의 값이 true이면 코드 실행이 종료되었다는 것을 의미 합니다.

true가 된 이후에 next 메소드를 사용하면 산출되는 값이 undefined 임을 알 수 있습니다.

제너레이터는 이터러블하기 때문에 for..of 반복문을 사용할 수 있으며, spread 문법도 사용이 가능합니다.

function* generate() {
	yield 5;
    yield 10;
    yield 15;
}

const generator = generate();

for(let value of generator) {
    console.log(value);
}
// 5
// 10
// 15
const spread = [0, ...generate()];
console.log(spread); // [0, 5, 10, 15]

아래의 코드는 1~5까지 숫자를 반환하는 이터러블을 만드는 코드입니다. range를 이터러블로 만들려면(for..of가 동작하도록 하려면) 객체에 Symbol.iterator(특수 내장 심볼)라는 메서드를 추가해 아래와 같은 일이 벌어지도록 해야 합니다.

  • for..of가 시작되자마자 for..of는 Symbol.iterator를 호출합니다(Symbol.iterator가 없으면 에러가 발생합니다).

  • Symbol.iterator는 반드시 이터레이터(iterator, 메서드 next가 있는 객체) 를 반환해야 합니다.

  • 이후 for..of는 반환된 객체(이터레이터)만을 대상으로 동작합니다.
    for..of에 다음 값이 필요하면, for..of는 이터레이터의 next()메서드를 호출합니다.

  • next()의 반환 값은 {done: Boolean, value: any}와 같은 형태이어야 합니다. done=true는 반복이 종료되었음을 의미합니다. done=false일땐 value에 다음 값이 저장됩니다.

let range = {
  from: 1,
  to: 5,

  // for..of 최초 호출 시, Symbol.iterator가 호출됩니다.
  [Symbol.iterator]() {
    // Symbol.iterator는 이터레이터 객체를 반환합니다.
    // for..of는 반환된 이터레이터 객체만을 대상으로 동작하는데, 이때 다음 값도 정해집니다.
    return {
      current: this.from,
      last: this.to,

      // for..of 반복문에 의해 각 이터레이션마다 next()가 호출됩니다.
      next() {
        // next()는 객체 형태의 값, {done:.., value :...}을 반환해야 합니다.
        if (this.current <= this.last) {
          return { done: false, value: this.current++ };
        } else {
          return { done: true };
        }
      }
    };
  }
};
// 객체 range를 대상으로 하는 이터레이션은 range.from과 range.to 사이의 숫자를 출력합니다.
console.log([...range]); // [1,2,3,4,5]

Symbol.iterator 대신 제너레이터 함수를 사용하면, 제너레이터 함수로 반복이 가능합니다.

let range = {
  from: 1,
  to: 5,

  *[Symbol.iterator]() { // [Symbol.iterator]: function*()를 짧게 줄임
    for(let value = this.from; value <= this.to; value++) {
      yield value;
    }
  }
};
console.log([...range]); // [1,2,3,4,5]

제너레이터는 이터레이터를 어떻게 하면 쉽게 구현할지를 염두에 두며 자바스크립트에 추가되었습니다.

제너레이터를 사용해 구현한 예시는 이터러블을 사용해 구현한 기존 예시보다 훨씬 간결합니다. 그리고 동일한 기능을 제공합니다.

제너레이터의 특수 문법 yield를 사용하면 제너레이터를 다른 제너레이터에 넣을 수도 있습니다. yield 지시자는 실행을 다른 제너레이터에 위임합니다. 반복을 수행하고 산출된 값을 전달 합니다.

function* generate(start, end) {
  for (let i = start; i <= end; i++) yield i;
}

function* generateCodes() {

  
  yield* generate(48, 57);
 // yield* generate(48, 57)와 같은 코드 
 // => for (let i = 48; i <= 57; i++) yield i;
  
}

let str = '';

for(let code of generateCodes()) {
  str += String.fromCharCode(code);
}

console.log(str); // 0123456789

Reference

[위키북스] 모던 자바스크립트 Deep Dive
https://ko.javascript.info/generators

profile
새로운 것을 기록하고 복습하는 공간입니다.

0개의 댓글