[Javascript] ES6에서의 순회와 이터러블 : 이터레이터 프로토콜

DaeHoon·2022년 6월 12일
0

Javascript

목록 보기
2/5

1. Array, Set, Map을 통해 알아보는 이터러블/이터레이터 프로토콜

  • 이터러블 (Iterable): 이터레이터를 리턴하는 Symbol.iterator()를 가진 값
  • 이터레이터 (iterator): {value, done} 객체를 리턴하는 next()를 가진 값
  • 이터러블/이터레이블 프로토콜: 이터러블을 for.. of, 전개 연사자 등과 함께 동작하도록한 규약

1-1 예제 코드

  const log = console.log;

  log('Arr -----------');
  const arr = [1, 2, 3];
  // iterator 할당
  let iter1 = arr[Symbol.iterator]();
  log(iter1)
  log(iter1.next())
  for (const a of iter1) log(a);
  for (const a of iter1) log(a);

  const arr1 = [4, 5, 6];
  // iterator를 null로 설정
  arr1[Symbol.iterator] = null;
  for (const a of arr1) log(a);

  //output
  Array Iterator {}
  {value: 1, done: false}
  2
  3
  {value: undefined, done: true}

  VM2897:1 Uncaught TypeError: arr is not iterable
      at <anonymous>:1:17
  • for .. of문은 iterable한 객체를 순회할 때 사용하는 문법이다. 즉, 인덱스를 이용한 순회가 아닌 Iterator를 이용해서 순회하는 방식이다.
  • iterator에 있는 next 메서드의 역할은 다음 요소에 읽어 올 요소가 있는지 확인한다. Array의 마지막 요소까지 불러온 다음에 next 메서드 실행 시, 값을 정의한 value의 값은 undefined, 끝을 알려주는 done의 값은 true가 된다. 이 후 for문으로 arr을 순회하려 해도 값이 출력되지 않는다.
  • arr[Symbol.iterator], 즉 arr의 이터레이터를 null로 할당한 뒤, for..of문 실행 시, iterable 하지 않다는 에러 로그를 출력한다.
  const log = console.log;

  log('Set -----------');
  const set = new Set([1, 2, 3]);
  let setIter = set[Symbol.iterator]();
  log(setIter) // SetIterator {1, 2, 3}
  for (const a of set) log(a);

  log('Map -----------');
  const map = new Map([['a', 1], ['b', 2], ['c', 3]]);
  for (const a of map.keys()) log(a);
  for (const a of map.values()) log(a);
  for (const a of map.entries()) log(a);
  • Set, Map도 마찬가지로 Iterable 객체이다.
  • Map 같은 경우 Key, values, entries 메서드 사용 시, Iterator를 반환한다. 그래서 위 코드와 같이 for.. of 문에서 사용이 가능하다.

2. 사용자 정의 이터러블을 통해 알아보기

2-1. 잘못된 이터러블

  const iterable = {
    [Symbol.iterator]() {
      let i = 3;
      return {
        next() {
          return i == 0 ? {done: true} : {value: i--, done: false};
        }
      }
    }
  };
  let iterator = iterable[Symbol.iterator]();

  log(iterator.next());
  log(iterator.next());
  log(iterator.next());

  //output 
  {value: 3, done: false}
  {value: 2, done: false}
  {value: 1, done: false}
  • 위와 같은 iterable 객체를 정의했다고 하자.
  • next 메서드를 정의해 값을 가져오지만 위와 같은 이터러블은 잘 만든 이터러블이라 할 수 없음,
for (const a of iterator) log(a);

error :VM2936:5 Uncaught TypeError: iterator is not iterable
    at <anonymous>:5:19
  • 이터레이터를 정의해주지 않아 for.. of문으로 실행 시, 이터레이터가 없다는 에러를 발생한다.
  • 콘솔로 iterator 변수를 찍어보면 {next: ƒ}가 출력되는데, next 메서드만 정의되어 있고 Symbol은 정의되어 있지 않아 위와 같은 에러가 출력 된다.

2-2 잘 만든 사용자 정의 이터러블

  const iterable = {
    [Symbol.iterator]() {
      let i = 3;
      return {
        next() {
          return i == 0 ? {done: true} : {value: i--, done: false};
        },
        [Symbol.iterator]() {
          return this;
        }
      }
    }
  };

  for (const a of iterator) log(a);
  //output 
  3
  2
  1

2-3 DOM에서의 이터러블

  for (const a of document.querySelectorAll('*')) log(a); // 브라우저의 모든 DOM을 가져와 출력함
  const all = document.querySelectorAll('*');
  let iter3 = all[Symbol.iterator](); // DOM도 이터러블한 객체다. -> [Symbol.iterator]()을 통해 이터레이터를 가져올 수 있다.
  log(iter3.next());
  log(iter3.next());
  log(iter3.next());

3. 전개 연산자

const a = [1,2];
log(...a, ...[3,4])

a[Symbol.iterator] = null
log(...a, ...[3,4])

//output
1, 2, 3, 4
error :Uncaught TypeError: object is not iterable (cannot read property Symbol(Symbol.iterator))
  • 전개 연산자는 이터러블 프로토콜을 따른다.
profile
평범한 백엔드 개발자

0개의 댓글