Postsjavascript

Iterator and Generator

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

21.10.174 min read
Iterator and 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의 특징은 단방향의 절차적 흐름을 가지고 있는 데이터를 처리하는데 용이하다는 생각이 든다.

More from javascript

View category
Proxy
javascript

Proxy

Javascript ES6 Proxy, Proxy, Trap, 프락시를 이용한 객체 조작의 제어

es6javascriptproxy7 min read

Comments