[Deep JavaScript] 심볼과 이터러블/이터레이터 프로토콜

Dae-Hee·2023년 7월 26일
0

Deep JavaScript Study

목록 보기
11/12
post-thumbnail

본 게시글은 인프런 함수형 프로그래밍과 JavaScript ES6+ 강의
개인적으로 공부하고 정리한 내용입니다.


▪︎ Symbol

ES6에서 추가된 생성자가 symbol 원시 값을 반환하는 내장 객체
충돌 위험이 없는 유일한객체의 프로퍼티 키(property key)를 만들기 위함이다.

아래 예시를 보자

  const list = [1, 2, 3];
  for (let i = 0; i < list.length; i++) {
    console.log(list[i]);
  }
  for (const a of list) console.log(a);
  // 리스트를 순회하고 출력되는 로그는 같다.

  const set = new Set([1, 2, 3]);
  for (const a of set) console.log(a);
  // 위 셋을 순회하고 출력되는 로그도 같다.
  for (let i = 0; i < set.length; i++) {
    console.log(set[i]); // undefined
  }
  // 하지만 set[i] = undefined
Array, Set, Map은 Symbol.iterator로 속성키를 사용한다.
  const arr = [1, 2, 3];
  console.log(arr[Symbol.iterator]);
  // f values() { ... } > 함수
  
  arr[Symbol.iterator] = null;
  for (const a of arr) console.log(a);
  // arr의 Symbol을 null로 초기화 하면 Error 발생
  // arr is not iterable

▪︎ 이터러블/이터레이터 프로토콜

  • 이터러블: 이터레이터를 리턴하는 Symbol.iterator 를 가진 값
    • 위 예시의 arrSymbol.iterator을 가지고 있기때문에 이터러블이다.
  • 이터레이터: { value, done } 객체를 리턴하는 next() 를 가진 값
    • arr[Symbol.iterator](); > 이터레이터
  • 이터러블/이터레이터 프로토콜: 이터러블을 for...of, 전개 연산자 등과 함께 동작하다록한 규약

이터러블/이터레이트 프로토콜 예시

  const arr = [1, 2, 3];
  let iterator = arr[Symbol.iterator]();
  
  iterator.next(); // { value: 1, done: false} 
  iterator.next(); // { value: 2, done: false}
  iterator.next(); // { value: 3, done: false}
  iterator.next(); // { value: undefined, done: true}
  iterator.next(); // { value: undefined, done: true}

  const map = new Map([['a', 1], ['b', 2], ['c', 3]]);
  const keys = map.keys(); // value가 map의 key인 iterator
  const values = map.values() // value가 map의 value인 iterator
  for (const a of values) console.log(a); // 1 > 2 > 3
  // values로 만든 이터레이터(MapIterator {1, 2, 3})를 
  // 심볼 이터레이터로 실행(next();) 했기때문에 value를 출력할 수 있다.

▪︎ 사용자 정의 이터러블

iterable변수는 [Symbol.iterator]라는 키를 가지고 있으며 이를 실행했을 때 이터레이터를 반환

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

▪︎ 잘 만들어진 이터러블(*well-formed iterable)

  • 이터레이터 객체의 Symbol.iterator 메서드가 이터레이터 자신을 반환해야 함을 의미
  • 이터레이터 객체를 다시 이터레이션할 수 있는 순환 가능한 객체
  const arr = [1, 2, 3];
  let iterator = arr[Symbol.iterator]();
  iterator.next();
  for (const a of arr) console.log(a);
  // 2, 3 만 출력
  
  [Symbol.iterator]() {
    return this;
  }
  // 위 속성을 추가하여 
  console.log(iterable[Symbol.iterator]() == iterable) // true
오픈소스뿐만 아니라 JS가 사용되고있는 환경인 브라우저에서 사용할 수 있는 
webAPIs에 구현되어있는 많은 값들이 이터러블/이터레이터 프로토콜을 따르고있다
  const all = document.querySelectorAll('*');  

  for (const a of all) console.log(a);
  // 순회가 가능하다 
  // 따라서,
  let iterator = all[Symbol.iterator]();
  console.log(iterator.next()); // <html></html>
  console.log(iterator.next()); // <head></head>
  console.log(iterator.next()); // <script></script>

▪︎ 전개 연산자

... 전개 연산자도 이터러블/이터레이터 프로토콜을 따르고 있는 값들을 펼치는 것이다.

  const list = [1, 2];
  list[Symbol.iterator] = null;
  console.log([...list, ...[1, 2, 3]]);
  // list Symbol을 null로 초기화 하면 Error 발생
  // list is not iterable


Reference

0개의 댓글