JS iterable, iterator(array VS generator)

강정우·2023년 3월 14일
0

JavaScript

목록 보기
33/53
  • 우선 iterator의 사전적 뜻은 반복자 이다. 그러다면 우선 반복문에 대하여 알아보자.
    ES5 : array.forEach 사용. break가 불가하다.
    ES6 : for (const item of array) 사용. 배열에서만 사용 가능하다(for - of 구문).
    ES6 : for (const item in object) 사용. 배열 외(객체 등)를 순회할 경우에 사용한다(for - in 구문).

  • 이때 ES6의 for in 구문은 index를 증가하면서 도는 것이 아닌 객체를 순회하며 반복문을 도는 것이다. 여기서 iterator가 등장한다.

iterable

  • property이고, iterator 함수가 구현되어 있으면 iterable하다고 부른다.
    즉, iterator는 객체를 순회하는 연산자이고, iterator 함수가 구현되어 있는 것이 iterable이다.

  • array도 iterable 하다고 볼 수 있다.

  • for of loop 를 사용하여 iterator item을 가져올 수 있다.

  • 배열에서 distructuring를 통하여 아이템을 추출하는 것도 가능하다.

iterator

  • 우선 python이나 js 같은 스크립트 언어를 사용하다보면 언어자체에서 배열과 iterator를 섞어서 쓸 수 있게 제공을 하고있다.

  • 쉽게 말해 우리가 iterator다~ 라고 하는 것은 iterable protocol과 iterator protocol을 두가지 모두 구현한 것이다. 그럼 우선 코드를 봐보자

const array = [1,2,3,4,5,6,7]

const iterator = (function(){
  let num = 1;
  
  return {
    next : function () {
      return (
        num > 7 ? 
        {done : true} : 
        {done : false value : num++}
      )
    }
  }
})
  • 위 코드는 array와 iterator모두 1~7를 뱉어내는 코드이다.

iterator와 array의 공통점

  • 둘다 for loop를 사용할 수 있다.
  • 아이템 조회가 가능하다.

iterator와 array의 차이점

  • iterator는 기본적으로 next 함수를 반환하는데 이 next 함수는 boolean 값의 doneany 타입의 value를 갖고있다. 즉, 이전에 얻어온 값 바로 다음 값만 가져올 수 있다.

  • 이때 iterator는 done은 끝이 났는지를 의미하고 value는 iterator가 아직 돌고있을 때 값을 반환한다.

  • 반면 array의 경우 random access가 가능하다. 즉, 인덱스를 갖고있는 어느 값이든 아무때가 가져오는게 가능하고 또한 .length도 바로 알 수 있다. 즉, array가 기능이 더 많기 때문에 아래 사진과 같다고 볼 수 있다.

  • 즉, 배열은 언제든 iterator로 변환할 수 있다. 반면, iterator는 array가 되려면 계산하지 않고 두던 모든 값을 계산해야한다. 즉, iterator의 next를 모두 불러서 iterator 순회를 다 끝내야 가능하다. 그것을 모아야 배열로 return이 가능하다.

그럼 array가 훨씬 편한데 왜 굳이? iterator가 존재할까?

iterator 존재 이유

  1. array가 기능이 많은 만큼 당연히 더 무거울 수 밖에 없다.
    즉, 코드 가독성과 코드 퍼포먼스가 떨어질 수 밖에 없다.

  2. array는 사용을 하려면 당연 array내의 모든 데이터를 메모리에 올려야한다.
    하지만 ierator같은 경우는 하나의 변수만 메모리에 올리면 되기 때문에 메모리를 방대하게 잡아먹는 이미지 같은 데이터를 처리할 때 array에 비해 매우 유리한 위치에 있다.

  3. 일정 규칙이 존재하는 수열과 같은 데이터를 다루기 위해서는 iterator를 사용하는것이 더 좋다.

함수형 프로그래밍에서의 iterator

map, reduce, filter

  • JS는 array에 위의 메서드가 자동으로 붙어서 나오는데 사실 iterator에서도 map, reduce, filter에 해당하는 로직을 갖다 붙이면 똑같이 구현 가능하다.
    이런 관점에서 볼 때 사실 map, reduce 함수들은 array보다 iterator에서 돌리는 것이 훨씬 이점이 많다고 볼 수 있다.

TS에서의 iterator 예제 코드

class CustomIterable implements Iterable<string> {
    private _arr: Array<string> = ['one', 'two']

    [Symbol.iterator]() {
        let nextIndex = 0

        return {
            next: () => {
                return {
                    value: this._arr[nextIndex++]
                    done: nextIndex < this._arr.length
                }
            }
        }
    }
}

const customIterable = new CustomIterable()

for (const item of customIterable._arr) {
    console.log(item) // 'one', 'two'
}

iterator와 generator

  • 사실 이 둘의 관계는 Promise와 async/await 처럼 generator가 iterator를 도와주는 모양이라고 볼 수 있다. 우선 밑의 코드를 봐보자.
function getIterator(){
  let num = 1;
  
  const next = () => (
    num > 7 ? 
    {done : true} : 
    {done : false, value : num++ }
  );
  
  return {
    [Symbol.iterator]: () => ({next}),
    next,
  };
}

function* getGenerator(){
  for (let num = 1; num <= 7; num++){
    yield num;
  }
}

for (const num of getIterator()){
  console.log(num);
} // 1 ~ 7 까지 print

[...getIterator()] // [1,2,3,4,5,6,7]
[...getGenerator()] // [1,2,3,4,5,6,7]
  • 위 코드를 보면 똑같이 작동하지만 iterator 코드가 더 복잡해보이고 gererator같은 경우는 function* 과 return 대신 yield 문법을 상용하면 된다.

iterator와 generator의 차이점

  • 엄밀히 말하면 generator가 iterator보다 좀 더 많은 기능을 제공한다.

  • iterator같은 경우는 for loop이나 distructuring 할 때 추가적인 작업이 필요하다.
    그것을 iterable protocol이라고 부른다. => Symbol.iterator

  • 반면 generator는 기본적으로 iterator protocol을 지원한다. 그렇다보니 실무에서는 iterator 보단 generator를 사용하는 편이 많기도 하다.

generator

  • iterator와 iterable의 조합이라고 말할 수 있다.

  • 우선 비슷한 예제를 봐보자.

Promise, async/await

  • 갑자기 웬 promise?! 할 수 있지만 이것은 기존 JS 함수 선언 규칙을 확장했다는 점에서 공통점을 갖는다.

  • 기존 JS는 return에 특정 값을 넣으면 함수는 그 해당 값을 반환한다.
    하지만 async/await는 조금 다르다. 함수 앞에 prefix로 async를 붙이면 해당함수는 더이상 return한 값을 그대로 반환하지 않는다. 대신 Promise 인스턴스를 반환한다.
    async는 키워드가 반환하는 타입이 달라졌다는 것을 암시하고 있는 것이다.

  • 마찬가지로 function 예약에 뒤에 * 가 붙으면 이것은 return type이 iterator라는 것을 알려주는 것이다.

즉, Promise와 async/await이 하는 일은 같지만 Promise를 보다 쉽게 사용하려고 async/await을 사용하는 것처럼
generator도 iterator를 보다 쉽게 사용하기위하여 사용하는 것과 같다.

reference

iterator VS array

iterator in TS

iterator VS generator

profile
智(지)! 德(덕)! 體(체)!

0개의 댓글