Code Logs

21.10.17

Javascript ES6 Iterator & Generator, 열거형, 제너레이터 함수, generator function, yield, function*
고양이 빵 Cat bread - 세마리의 고양이와 홈베이킹 이야기

Iterator and Generator

Javascript ES6 Iterator & Generator, 열거형, 제너레이터 함수, generator function, yield, function*

Table of contents

  1. Iterable and Iterator
    1. Iterable
    2. iterator
  2. Generator

Iterable and Iterator

개발을 하다 보면 이런 에러 메시지를 마주치곤 한다.

TypeError: variable is not iterable

variable이 반복될 수 없다는 의미인데, 정확히 iterable이 어떤 것인지 그리고 iterator는 어떤 것인지 정리한다.

Iterable

Array, Map과 같이 for..of문 내에서 반복 되는 것 처럼 어떤 반복 동작이 가능한 경우 iterable이라 할 수 있다. javascript에는 이미 내장되어 있는 iterable한 객체들이 몇 있는데 이런 것을 내장 iterable이라 한다.

내장 iterable:

  • String
  • Array
  • TypedArray
  • Map
  • Set

내장 iterable 외에 사용자가 iterable을 직접 정의해서 사용 할 수 있는데 사용자가 iterable을 직접 정의하기 위해서는 아래의 구조적인 규칙을 반드시 준수 해야한다.

  • iterable 객체는 Symbol.iterator를 키로하는 @@iterator 메서드를 갖고 있어야 함.

Iterator

Iterator는 Iterator protocol을 따르는 객체로, Iterator protocol은 아래의 규칙을 의미한다.

  • next 메서드를 갖고 있어야 한다.
    • next 메서드는 호출시 { done: boolean, value: any } 형태의 값을 반환해야 한다.
  • Symbol.iterator를 키로하고 iterator를 리턴하는 메서드를 갖고 있어야 한다.
const adder = {
  value: 0,
  increment: 2,
  limit: 10,
  next: function () {
    if (this.value >= this.limit) return { done: true }
    return { done: false, value: (this.value += this.increment) }
  },
  [Symbol.iterator]: function () {
    return this
  },
}

adder라는 객체를 정의 하였다. addervalue의 초기값 0을 기준으로 매번 반복동작이 수행 될 때 (next 메서드가 호출 될 때) 마다 increment 만큼 값이 증가하고 valuelimit 보다 크거나 같을 경우 더이상 반복동작을 수행 할 수 없다.

이와 같이 사용자가 직접 iterable한 객체를 정의 할 수 있다. 구조적으로 조건에 충족하는 iterator를 구현하면 아래와 같은 문법을 통해 해당 객체를 순회 할 수 있다.

for (const value of adder) {
  console.log(value)
}

/* 실행 결과:
  2
  4
  6
  8
  10
*/

당연히 iterator를 통해 값을 순회 할 경우 내부 변수의 값이 변경되기 때문에 순회를 이미 마친 객체는 일반적인 방법으로는 다시 처음의 값으로 되돌릴 수 없다.

for (const value of adder) {
  console.log(value)
}

/* 실행 결과:
  2
  4
  6
  8
  10
*/

console.log('Value', ...adder)
/* 실행 결과:  
  Value
*/

Generator

Iterator를 직접 정의하여 사용 할 수 있지만 Generator 함수를 이용하면 조금 더 간편하게 iterator를 구현 할 수 있다.

Generator 함수는 일반 함수 정의와 달리 function 키워드 뒤에 *를 붙여야 한다.

Arrow function 정의 방식을 통해서는 generator를 구현 할 수 없음

iterator에서 next 메서드를 호출하는 것은 반복구문을 1회 수행한다는 것을 의미하는데 Generator 함수에서는 yield 키워드를 통해 함수 반복구문의 범위를 설정 할 수 있다.

아래 예제 코드를 보면 앞서 설명한 이야기를 조금 더 쉽게 이해 할 수 있다.

function* adder() {
  let value = 0
  const increment = 2
  const limit = 10

  value += increment
  yield value

  value += increment
  yield value

  value += increment
  yield value

  value += increment
  yield value

  value += increment
  yield value
}

for (const value of adder()) {
  console.log(value)
}

/* 실행 결과:
  2
  4
  6
  8
  10
*/

Generator 함수 내부에서 루프문을 이용하면 조금더 쉽게 반복구문을 정의 할 수 있다.

function* adder() {
  let value = 0
  const increment = 2
  const limit = 10

  while (value < limit) {
    yield (value += increment)
  }
}

for (const value of adder()) {
  console.log(value)
}

/* 실행 결과:
  2
  4
  6
  8
  10
*/

개인적으로 Generator 함수를 사용하여 실무에서 무언가 작업한 경험은 극히 드물다 . 반복적인 작업을 처리할 때 IteratorGenerator를 떠올리기 보단 Array를 통해 간단히 해결하는 것이 대부분인 것 같다. 그런데 순차적인 반복구문의 수행 그리고 일정 범위에 도달 했을 때의 반복 불가와 같은 Iterable의 특징은 단방향의 절차적 흐름을 가지고 있는 데이터를 처리하는데 용이하다는 생각이 든다.

카테고리 더보기

    댓글