본문 바로가기

JavaScript

[Deep dive] 34장 이터러블

34.1 이터레이션 프로토콜

 

- 순회 가능한(iterable) 데이터 컬렉션을 만들기 위해 ESMAScript 사양에서 정의하여 미리 약속한 규칙 

- 순회 가능한 데이터 컬렉션을 이터레이션 프로토콜을 준수하는 이터러블로 통일하여 for...of문, 스프레드 문법, 배열 디스트럭처링 할당의 대상으로 사용할 수 있도록 일원화 

 

34.1.1 이터러블 

- 이터러블 프로토콜을 준수한 객체 

- Symbol.iterator를 프로퍼티 키로 사용한 메서드를 직접 구현 or 프로토타입 체인을 통해 상속받은 객체 

const isIterable = v => v !== null && typeof v[Symbol.iterator] === 'function';

//배열,문자열,Map,Set 등은 이터러블
isIterable([]); //true
isIterable(' '); //true
isIterable(new Map()); //true
isIterable(new Set()); //true
isIterable({}); //true

- 이터러블은 for...of문으로 순회할 수 있으며, 스프레드 문법과 배열 디스트럭처링 할당의 대상으로 사용 가능

const array = [1,2,3];

//배열은 Array.prototype의 Symbol.iterator 메서드를 상속받는 이터러블
console.log(Symbol.iterator in array); //true

//이터러블인 배열은 for..of문으로 순회 가능
for(const item of array) {
  console.log(item);
}

//이터러블인 배열은 스프레드 문법의 대상으로 사용 가능
console.log([...array]); //[ 1, 2, 3 ]

//이터러블인 배열은 배열 디스트럭처링 할당의 대상으로 사용 가능
const [a, ...rest] = array;
console.log(a,rest); //1 [ 2, 3 ]

- 일반 객체도 이터러블 프로토콜을 준수하도록 구현하면 이터러블이 됨

 

34.1.2 이터레이터 

- 이터러블의 Symbol.iterator 메서드를 호출하면 이터레이터 프로토콜을 준수한 이터레이터를 반환

- 이터러블의 Symbol.iterator 메서드가 반환한 이터레이터는 next 메서드를 가짐

- 이터레이터의 next 메서드는 이터러블의 각 요소를 순회하기 위한 포인터의 역할 

- next 메서드를 호출하면 이터러블을 한 단계씩 순회하며 순회 결과를 나타내는 이터레이터 리절트 객체(iterator result object)를 반환 

//배열은 이터러블 프로토콜을 준수한 이터러블
const array = [1,2,3];

//Symbol.iterator 메서드는 이터레이터를 반환. 이터레이터는 next 메서드를 가짐
const iterator = array[Symbol.iterator]();

//next 메서드를 호출하면 이터러블을 순회하며 순회 결과를 나타내는 이터레이터 리절트 객체 반환 
//이터레이터 리절트 객체는 value와 done 프로퍼티를 갖는 객체 
console.log(iterator.next()); //{ value: 1, done: false }
console.log(iterator.next()); //{ value: 2, done: false }
console.log(iterator.next()); //{ value: 3, done: false }
console.log(iterator.next()); //{ value: undefined, done: true }

- 리절트 객체의 value 프로퍼티 

 : 현재 순회 중인 이터러블의 값

 

- 리절트 객체의 done 프로퍼티 

 : 이터러블의 순회 완료 여부 

 

34.2 빌트인 이터러블

- 자바스크립트가 제공한 이터레이션 프로토콜을 준수한 객체인 빌트인 이터러블

- Array, String,Map,Set, TypeArray, arugments, DOM 컬렉션 

 

34.3 for ... of 문

- 이터러블을 순회하면서 이터러블의 요소를 변수에 할당

for(변수선언문 of 이터러블) { ... }

- 내부적으로 이터레이터의 next 메서드를 호출하여 이터러블을 순회하며 next 메서드가 반환한 이터레이터 리절트 객체의 value 프로퍼티 값을 for...of문의 변수에 할당 

for(const item of [1,2,3]) {
  console.log(item); //1 2 3
}

 

34.4 이터러블과 유사 배열 객체 

- 유사 배열 객체 

 : 마치 배열 처럼 인덱스로 프로퍼티 값에 접근 가능하면서 length 프로피터를 갖는 객체 

 : 이터러블이 아닌 일반 객체 

 : Symbol.iterator 메서드가 없기 때문에 for ... of문으로 순회할 수 없음

 

- arguments, NodeList, HTMLCollection은 유사 배열 객체이면서 이터러블 

 

- 이터러블이 아닌 유사 배열 객체는 Array.from 메서드를 사용하여 배열로 변환 가능 

 

34.5 사용자 정의 이터러블 

 

34.5.1 사용자 정의 이터러블 구현 

- 일반 객체도 이터레이션 프로토콜을 준수하도록 구현하면 사용자 정의 이터러블이 됨

- 이터레이션 프로토콜을 준수하도록 Symbol.iteraotr 메서드를 구현하고 Symbol.iterator 메서드가 next 메서드를 갖는 이터레이터를 반환하도록 함

- 이터레이션의 next 메서드는 done과 value 프로퍼티를 가지는 이터레이터 리절트 객체를 반환

- for...of 문은 done 프로퍼티가 true가 될 때까지 반복하며 done 프로퍼티가 true가 되면 반복을 중지

//피보나치 수열을 구현한 사용자 이터러블
const fibonacci = {
  //[Symbol.iterator] 메서드를 구현
  [Symbol.iterator]() {
    let [pre,cur] = [0,1];
    const max = 10; //수열의 최대값

   //next 메서드를 소유한 이터레이터를 반환
   //next 메서드는 이터레이터 리절트 객체를 반환
   return {
    next() {
      [pre,cur] = [cur,pre+cur];
      return {value:cur , done: cur>= max};
    }
   };
  }
};

//이터러블인 fibonacci 객체를 순회할 때마다 next 메서드가 호출
for(const num of fibonacci) {
  console.log(num); //1 2 3 5 8
}

 

34.6.2 이터러블을 생성하는 함수 

- 수열을 최대값을 인수로 전달받아 이터러블을 반환하는 함수 

- fibonacciFunc 함수는 이터러블을 반환 

//피보나치 수열을 구현한 사용자 정의 이터러블을 반환하는 함수 
//수열의 최대값을 인수로 전달받음 
const fibonacciFunc = function (max) {
  let [pre,cur] = [0,1];

  //Symbol.iterator 메서드를 구현한 이터러블 반환
  return {
    [Symbol.iterator]() {
      return {
        next () {
          [pre,cur] = [cur, pre+cur];
          return {vlaue: cur, done: cur>=max};
        }
      };
    }
  };
};

//이터러블을 반환하는 함수에 수열의 최대값을 인수로 전달하면서 호출
for(const num of fibonacciFunc(10)) {
  console.log(num); //1 2 3 5 8
}

 

34.6.3 이터러블이면서 이터레이터인 객체를 생성하는 함수 

- 이터레이터를 생성하려면 이터러블의 Symbol.iterator  메서드를 호출해야함

- 이터러블이면서 이터레이터인 객체를 생성하면 Symbol.iterator 메서들르 호출하지 않아도 됨

//이터러블이면서 이터레이터인 객체 
//이터레이터를 반환하는 Symbol.iterator 메서드와 이터레이션 리절트 객체를 반환하는 next 메서드를 소유 
{
 [Symbol.iterator] () {return this;},
 next() {
  return { value: any, done : boolan};
 }
}
//이터러블이면서 이터레이터인 객체를 반환하는 함수
const fibonacciFunc = function (max) {
  let [pre,cur] = [0,1];

  //Symbol.iterator 메서드를 구현한 이터러블 반환
  return {
    [Symbol.iterator]() { return this;},
    //next 메서드는 이터레이터 리절트 객체를 반환 
    next() {
      [pre,cur] = [cur,pre+cur];
      return {valeu:cur, done: cur>=max};
    }
  };
};

//iter는 이터러블이면서 이터레이터
let iter = fibonacciFunc(10);

//iter는 이터러블이므로 for..of문으로 순회 가능
for(const num of iter) {
  console.log(num); //1 2 3 5 8
}

iter = fibonacciFunc(10);

//iter는 이터레이터이므로 이터레이션 리절트 객체를 반환하는 next 메서드 소유
console.log(iter.next()); //{ valeu: 1, done: false }
console.log(iter.next()); //{ valeu: 2, done: false }
console.log(iter.next()); //{ valeu: 3, done: false }
console.log(iter.next()); //{ valeu: 5, done: false }
console.log(iter.next()); //{ valeu: 8, done: false }
console.log(iter.next()); //{ valeu: 13, done: true }

 

36.6.4 무한 이터러블과 지연 평가 

- 무한 이터러블을 생성하는 함수 

- 이터러블은 지연 평가 (Jazy evaluation)을 통해 데이터를 생성 

//무한 이터러블을 생성하는 함수 
const fibonaccifFunc = function() {
  let [pre,cur]  = [0,1];

  return {
    [Symbol.iterator] () {return this;},
    next() {
      [pre,cur] = [cur,pre+cur];
      return {value:cur};
    }
  };
};

//fibonaccifFunc 함수는 무한 이터러블을 생성
for(const num of fibonaccifFunc()) {
  if(num > 10000) break;
  console.log(num); //1 2 3 5 8 ... 6765
}

//배열 디스트럭처링 할당을 통해 무한 이터러블에서 3개의 요소만 취득
const [f1, f2, f3] = fibonaccifFunc();
console.log(f1,f2,f3); // 1 2 3

- fibonacciFunc 함수는 데이터 소비자인 for...of문이나 배열 디스트럭처링 할당 등이 실행되기 이전까지 데이터를 생성하지는 않음. for ... of문의 경우 이터러블을 순회할 때 내부에서 이터레이터의 next 메서드를 호출하는데 이때 데이터가 생성

 => 데이터가 필요할 때까지 데이터의 생성을 지연하다가 데이터가 필요한 순간 데이터를 생성 

 

'JavaScript' 카테고리의 다른 글

[Deep dive] 36장 디스트럭처링 할당  (0) 2023.08.18
[Deep dive] 35장 스프레드 문법  (0) 2023.08.18
[Deep dive] 33장 7번째 데이터 타입 Symbol  (0) 2023.08.17
[Deep dive] 32장 String  (0) 2023.08.17
[Deep dive] 31장 RegExp  (0) 2023.08.17