GRAMMAR

Public Notification
  • settings_backup_restore
  • bug_report

Iteration protocols

ECMAScript 2015(ES6)에는 새로운 문법이나 빌트인(built-in) 오브젝트 뿐만이 아니라, 프토토콜(protocol: 규약)이라는 메커니즘(mechanism)이 추가되었다. 프로토콜은 정해진 규칙만 충족한다면 어떠한 객체에 의해서도 구현될 수 있다. 대표적으로 이터레이블(iterable) 프로토콜과 이터레이터(iterator) 프로토콜이 있다.

이터레이션(Iteration) 프로토콜(protocols)

이터레이션 프로토콜은 이터러블(iterable) 프로토콜과 이터레이터(iterator) 프로토콜을 함께 부르는 용어이다. 여기서 프로토콜은 규약을 의미한다. 일정 형식의 반복되는 데이터를 다루기 위한 처리 규약이며 이 규약에 맞춰진 객체는 공통으로 관련 문법을 적용받을 수 있다. 이터레이션 프로토콜은 다양한 반복 데이터 처리 방식을 단일 방식으로 통일시키며 코드간 호환 체계를 유지할 수 있다.

이터러블(iterable) 프로토콜(protocol)

이터러블은 "반복 가능한"이라는 의미를 가진 용어로서 반복 가능한 객체를 설명할 때 사용된다. 보통 자료 구조를 다루는 객체는 이터러블 프로토콜을 준수 한다고 할 수 있다. 이러터블 프로토콜을 준수하는 객체를 추상적으로 이터러블 객체라고 부른다. 이터러블 객체는 for...of 반복 구문이 사용 가능해야 한다. 그렇지 않다면 이터러블 객체가 아니다. 예를 들어서 Object의 키를 for...of 반복 구문으로 접근을 시도하면 에러가 발생한다. 그렇기 때문에 Object는 이터러블 객체가 아니다.

다음의 내장 객체들은 이터러블 프로토콜을 준수한다.

DOM 인터페이스에서는 다음의 자료 타입에서 이터러블 프로토콜을 준수한다.

이터러블 객체들은 다음의 기술적인 처리가 가능하다.

  • for...of 반복문 사용
  • 스프레드(spread) 연산자의 사용
  • 구조 분해할당(destructuring assignment) 사용
  • 이터러블 객체를 인수로 사용하는 내장 함수 또는 메소드의 사용

그렇다면 내장된 이터러블 객체 말고 직접 이터러블 객체를 구현할 수는 없을까? 직접 이터러블 객체를 구현하기 위해서는 제너레이터(generator) 함수를 구현하면 된다. 제너레이터 함수는 리터럴과 Generator 객체를 통해서 구현할 수 있다. Generator 객체 또는 제너레이터 함수를 참고하기 바란다.

이터레이터(iterator) 프로토콜(protocol)

이터레이터 프로토콜 역시 이름에서 의미하듯이 반복과 관련된 규약이다. 반복 가능한이 아닌 스스로 반복자로 불리는 존재이다. 이터러블 객체는 반복되는 데이터에 대한 인덱스를 가지고 있으며 이러한 각 객체의 반복 처리와 관련된 API가 존재한다. 반면 이터레이터 객체는 지정된 사양에 따라 구현되도록 강제한다. 이러한 규약을 준수하는 객체가 이터레이터 객체이다.

이터레이터 객체의 표준 사양은 다음과 같다.

next() 메소드의 구현(필수)
필수 구현 사항으로 0개 또는 1개의 인수를 받을 수 있다. 그리고 다음 두 개의 프로퍼티를 갖는 IteraotrResult 인터페이스를 준수하는 객체를 반환해야 한다.
  • done : 선택 사항으로 반복자가 시퀀스의 다음 값을 생성할 수 있는 경우 false를 갖는다. 반대로 반복이 종료되는 경우 true를 갖는다. true를 반환하는 경우 next() 후속 호출이 있는 경우 true를 반환할 것으로 예상한다.
  • value : 선택 사항으로 반복자가 시퀀스에서 현재 생성된 값을 지정한다. donetrue인 경우 생략될 수 있다.
실제로 두 개의 프로퍼티가 엄격하게 요구되지는 않는다. 선택적으로 모두 생략된다면 사실상 아래와 같이 동일하게 처리한다.
{ done: false, value: undefined }
return() 메소드 구현(선택)
0개 또는 1개의 인수를 받고IteratorResult 인터페이스를 준수하는 객체를 반환한다. 이 메소드가 호출되면 반복자는 더 이상 next() 메소드를 호출하지 않겠다는 의미로 받아들인다.
throw() 메소드 구현(선택)
0개 또는 1개의 인수를 받고 IteratorResult 인터페이스를 준수하는 객체를 반환한다. 일반적으노 donetrue이다. 필요한 경우 이 메소드를 호출하여 반복자에게 오류가 감지되었음을 알린다.

이터레이터 프로토콜을 준수하는 객체는 일반적으로 다음의 객체 구조를 따른다.

const iterator = {
  next() {
    // codes..
  },
  [Symbol.iterator]() {
    return this;
  }
};

통상 이터러블 객체는 이터레이터 프로토콜을 준수한다. 다시 말해 이터러블 객체는 이터레이터 객체를 반환하는 메소드를 가지고 있다. 그런데 이 메소드를 호출하는 키가 상기 코드에서 볼 수 있듯이 [Symbol.iterator]()로 정해져 있다. 이터러블 객체는 잘 알려진 심벌(well-know symbol)인 Symbol.iterator를 키로 사용하여 구현한 메소드에서 이터레이터 프로토콜을 준수하는 이터레이터 객체를 반환해야 한다.

@@iterato

Symbol.iterator 키를 사용하여 구현한 메소드를 호출하는 표현적인 방법이 @@iterator이다. 실제 코드에서는 해당 객체의 인스턴스를 통해 [Symbol.iterator]()로 호출된다.

비동기 이터레이션(async iteration) 프로토콜

비동기 이터레이션 프로토콜은 비동기 이터러블 프로토콜과 비동기 이터레이터 프로토콜을 지칭한다. 다음의 사항을 제외하고 동기 이터레이션 프로토콜과 동일하다.

  • 반복자에서 반환되는 값은 Promise 객체로 래핑되어 Promise 객체로 반환된다. 다시 말해 IteratorResult 인터페이스를 준수하는 객체가 Promise 객체로 래핑되어 반환된다.
  • Symbol.asyncIterator 키로 비동기 반복자를 반환하는 메소드를 구현하고 호출한다. 표현적인 방법은 @@ayncIterator이다.
  • AsyncGenerator 객체 또는 비동기 제너레이터 함수를 사용하여 비동기 반복자를 구현한다.

정리

표준 기술 사양에는 이터러블 프로토콜을 준수하는 객체가 반드시 이터레이터 객체를 반환해야 한다는 조건은 명백하지 않다. 다만 내장된 이터러블 객체들은 이터레이터 객체를 반환하는 규약을 지키고 있고 Generator 객체 또는 제너레이터 함수를 통해서 이터러블과 이터레이터 프로토콜을 동시에 준수하는 객체를 생성하기에 이터러블 객체가 이터레이터 객체를 반환하는 것은 암묵적인 강제 사항으로 봐야할 듯 하다.