자바스크립트

Section 10. 자바스크립트 이터러블

포칼이 2023. 4. 7. 12:01

I. 이터러블 프로토콜 iterable protocol

  • 반복, 순회 기능을 사용하는 주체간의 통일된 규격이다.
  • 공통 기능들 : for ... of, 스프레드 문법, 배열 디스트럭쳐링

 

이터러블 iterable - 이터러블 프로토콜을 준수하는 객체

  • 배열, 문자열, Set, Map, arguments 등 ...
  • 키 Symbol.iterator (well-known 심볼)의 값으로 이터레이터를 반환하는 메서드를 가진다.

*Symbol.iterator같이 잘 알려져서 많은 것들의 식별자로 사용되는 것을 well-known 심볼이라 한다.

 

배열 하나를 출력해보자.

[]

그리고 콘솔에서 그걸 펼쳐보면 다음과 같다.

프로토타입 밑에 Symbol.iterator를 키로하는 어떤 함수가 들어있는 것을 볼 수 있다.

이 프로퍼티가 이터레이터의 핵심이다.

console.log(
  [][Symbol.iterator],
  ''[Symbol.iterator],
  new Set()[Symbol.iterator],
  new Map()[Symbol.iterator]
);

배열, 문자열, Set, Map에 대한 Sysmbol.iterator의 값을 출력해봤다. 출력문은 다음과 같다.

공통적으로 어떤 함수들이 출력 되는 것을 볼 수 있다.

이런 함수들은 이터러블이 아닌 함수에는 존재하지 않는다.

// 다른 타입의 인스턴스에는 없음
console.log(
  (1)[Symbol.iterator],
  (true)[Symbol.iterator],
  { x: 1 }[Symbol.iterator]
); //undefined undefined undefined

 

이 함수들을 실행 괄호 ()를 붙여서 실행해보면

console.log(
  [][Symbol.iterator](), //Array Iterator {}
  ''[Symbol.iterator](), //StringIterator {}
  new Set()[Symbol.iterator](), //SetIterator {}
  new Map()[Symbol.iterator]() //MapIterator {}
);

각각 해당하는 자료형의 이터레이터를 반환하는 것을 알 수 있다.

 

II. 이터레이터 iterator

next 메서드를 통해 이터러블을 순회하며 값을 반환한다.

const arr = [1, 'A', true, null, {x: 1, y: 2 }];
const arrIterator = arr[Symbol.iterator]();

console.log(arrIterator);

arr의 심볼 이터레이터에 접근해서 arrIterator를 뽑아냈다.

그리고 그것을 출력해보면 next()라는 함수를 가지고 있는 것을 볼 수 있다.

arr[Symbol.iterator]()를 통해서 반환된 Array Iterator 객체는 next라는 함수(기능)를 가지고 있는 것이다.

이 기능을 이용하면 arr의 요소들을 차례대로 뽑아낼 수 있다.

arrIterator.next(); //{value: 1, done: false}
arrIterator.next(); //{value: 'A', done: false}
arrIterator.next(); //{value: true, done: false}
arrIterator.next(); //{value: null, done: false}
arrIterator.next(); //{value: {…}, done: false}
arrIterator.next(); //{value: undefined, done: true}

 

이터레이터 프로토콜 iterator protocol

next 메서드의 반환 객체 내 요소는 다음과 같다

  • value - 해당 차례에 반환할 값
  • done - 순회 종료 여부 (마지막 값 반환 다음 차례부터)

III. 이터러블 만들어보기

주사위를 열 번 굴리는 이터러블

const diceTenTimes = {
  // ⭐️ 아래의 메서드를 갖는 것이 이터러블 프로토콜
  [Symbol.iterator] () {
    let count = 0;
    let maxCount = 10;
    
    // ⭐️ 이터레이터(next 메서드를 가진 객체)을 반환
    return {
      next () {
        return {
          value: Math.ceil(Math.random() * 6),
          done: count++ >= maxCount
        }
      }
    }
  }
}

diceTenTimes라는 객체를 하나 만들었다.

이 객체에 Symbol.iterator를 키로 하려면 [ ]를 써줘야 한다. 거기에 실행 괄호가 붙어서  심볼 이터레이터라는 메서드가 실행 된다. 

이 메서드는 이터레이터 객체를 반환한다. 그 객체에는 next라는 함수가 있고 그 함수가 반환하는 것은 

value: Math.ceil(Math.random() * 6),

done: count++ >= maxCount

이것이 되는 것이다. 

 

실행을 해보면 다음과 같다

const diceIterator = diceTenTimes[Symbol.iterator]();

for (let i = 0; i < 12; i++) {
  console.log(
    diceIterator.next()
  );
}

여기서 next의 done 프로퍼티가 true를 반환 했는데도 계속 출력된 이유는 for문의 종료 조건때문이다. 

 

for ... of 문도 사용이 가능하다.

// 💡 for ... of 문 사용 가능
for (const num of diceTenTimes) {
  console.log(num);
}

for 문과는 다르게 딱 10번 출력된 것을 볼 수 있다. 

스프레드 문법도 사용 가능하다.

const diceResults = [...diceTenTimes];

console.log(diceResults); //[5, 1, 5, 4, 3, 3, 4, 1, 1, 2]

 

*이 예시는 제너레이터로 보다 간편하게 구현이 가능하다.