[JS] 이터러블과 이터레이터

박시은·2023년 7월 7일
0

JavaScript

목록 보기
36/58
post-thumbnail

ES6 이전에 기존의 반복문 for, forEach, for-in들은 객체를 순회할 때 많은 문제점이 있었다. (제어문 - 조건문과 반복문)

  • for: 배열의 길이를 통해 구한 index에 의존에 순회하기 때문에, 해당 객체에 대한 length 정보가 필요하고 순회하기 위해 인덱스 변수를 선언해서 사용해야한다. (단순히 배열을 순회하려는 목적에 비해서 많은 작업이 필요)
const array = [1, 2, 3];

for (let i = 0; i < array.length; i++) {
  const element = array[i];

  console.log(element); // 1, 2, 3
}

  • forEach: forEach 메서드는 배열에서만 사용할 수 있으며, 객체의 프로퍼티를 순회할 수 없다. 또한 break나 continue와 같은 제어문을 사용할 수 없어서 순회 중간에 반복을 멈추거나 건너뛸 수 없다.
const color = ["red", "pink"];

color.forEach(function (key) {
  console.log(`내가 좋아하는 색깔은 ${key}입니다!`);
  // 내가 좋아하는 색깔은 red입니다!
  // 내가 좋아하는 색깔은 pink입니다!
});

  • for-in: 주로 객체의 속성을 순회할 때 사용되며, 배열에는 사용되지 않는다.
const school = {
  school_name: "인삼대학교",
  student_number: "3000명",
};
for (const key in school) {
  console.log(`${key}의 속성값은 ${school[key]} 입니다.`);
  // school_name의 속성값은 인삼대학교 입니다.
  //student_number의 속성값은 3000명 입니다.
}

따라서 ES6에서는 이러한 문제점을 해결하기 위해 for-of라는 순회 방법을 도입하였다.

  • for-of문은 length 기반의 순회외는 달리 배열 그 자체만으로 순회를 할 수 있다는 특징이 있다.
  • 또한 배열에서 element를 가져오겠다는 의도가 코드에 보이므로 선언적이라고 할 수 있다.
const array = [1, 2, 3];

for (const element of array) {
  console.log(element); // 1, 2, 3
}

for-of 반복문을 통해 객체를 순회할 수 있는 이유는 이터레이션 프로토콜(=이터레이션 인터페이스) 때문이다.


▶ 이터레이션 프로토콜

순회 가능한 자료구조를 만들기 위한 프로토콜을 말하며 ES6에서 도입되었다.

ES6 이전의 순회 가능한 자료구조들은 통일된 규약 없이 각자 나름의 구조를 가지고 다양한 방법으로 순회하였다.

ES6 에서는 순회 가능한 자료구조를 이터러블로 통일하여 배열, 문자열 등과 같은 이터러블 객체를 일관된 방식으로 순회할 수 있도록 하였다.


이터레이션 프로토콜은 두 가지 주요한 요소로 구성된다.


▷ 이터러블(Iterable)

이터러블 객체는 이터레이터를 리턴하는 [Symbol.iterator]() 메서드를 가진 객체이다. (설명아래)

  • 배열과 문자열 역시 Symbol.iterator을 상속받기 때문에 이터러블 객체이다.
  • 위에서 설명한 for-of 역시 이터러블을 활용한 것이다.
  • for-of 외에도 Spread 연산자도 이터러블을 활용할 수 있다.
  • 즉, 반복할 수 있는 객체이다.

▷ 이터레이터 (Iterator)

이터레이터란 이터러블 객체의 요소를 순회하기 위한 객체를 말한다. (반복 가능한 객체)

  • for of 구문을 사용하기 위해선 컬렉션 객체가 [Symbol.iterator] 속성을 가지고 있어야한다. (직접 명시 가능)

배열한테 이터러블 표식을 없애보자

let arr = [1, 2, 3];
for (const a of arr) console.log(a); // 1,2,3

arr[Symbol.iterator] = null;
for (const a of arr) console.log(a); // TypeError: arr is not iterable
  • Symbol.iterator를 null로 설정하면, 배열은 이터레이터를 생성할 수 없으므로 "TypeError: arr is not iterable"과 같은 에러가 발생한다.

  • Symbol.iterator(이터레이터 심벌)는 이터레이터를 반환하는 메서드이다.

  • 이터레이터 심볼이 없는 객체는 for-of과 같은 이터레이터 기반의 반복 구문에서 사용할 수 없다.


이터레이터는 아래 항목을 만족시키는 객체이다.

  • 이터레이터는 next() 메서드로 순환할 수 있다.
  • next() 메서드는 { value, done } 형태의 객체를 반환한다.
  • value는 현재 요소의 값을 가지며, done은 반복이 완료되었는지를 나타내는 불리언 값이 저장된다.
const arr = [1, 2, 3];
const iter = arr[Symbol.iterator]();
printArr = iter.next();

console.log(printArr); // { value: 1, done: false }

for-of 문 없이 이터레이터를 반복실행

let a = [1, 2, 3];

let iter = a[Symbol.iterator](); // 변수 iter에 이터레이터 대입

while (true) {
  let iteratorResult = iter.next();
  if (iteratorResult.done === true) break;
  let v = iteratorResult.value;
  console.log(v); // 1, 2, 3
}

for-of문 사용

let a = [1, 2, 3];

for (let v of a) console.log(v); // 1, 2, 3
}

이처럼 for-of 문 역시 Symbol.iterator 메서드를 가졌기 때문에 이터러블(반복가능)한 객체라고 할 수 있다.


이터러블과 이터레이터의 관계는 다음과 같다.




▶ 이터레이터 vs 배열

이터레이터는 배열의 부분 집합이다!

이터레이터는 next() 메서드를 통해 이전에 얻어온 값 바로 다음 값만 얻어올 수 있지만, 배열은 인덱스를 가지고 어느 곳에 있는 값이든 아무때나 가져오는게 가능하다. 또한 배열의 아이템의 총 개수도 바로 알 수 있다.

배열은 언제든 이터레이터로 변환할 수 있다.
하지만, 이터레이터를 배열로 전환하려면 next를 모두 불러 이터레이터 순회를 다 끝내야 가능하다.

//배열

const arr = [1, 2, 3];
// arr[0] == 1, arr[2] == 3
// arr.lenhth == 3

console.log(arr[0]); // 1
console.log(arr[1]); // 2
console.log(arr[2]); // 3
//이터레이터

const iterator = (function () {
  let num = 1;

  return {
    next: function () {
      return num > 3 ? { done: true } : { done: false, value: num++ };
    },
  };
})();

console.log(iterator.next().value); // 1
console.log(iterator.next().value); // 2
console.log(iterator.next().value); // 3

이터레이터는 다음 아이템을 가져오는 것 외에 쓸 데가 없어보인다.
이터레이터가 배열의 부분집합이라면 배열만 사용하면 되는 것이 아닐까?


이터레이터는 배열보다 메모리 소모가 적다.

기능이 많은 것이 항상 좋은 것이 아니다. 기능이 많은 배열은 무거울 수 밖에 없다.
또한 배열을 이용하려면 모든 아이템들을 메모리에 올려놓아야하지만, 이터레이터는 코드 부분을 제외하면 변수 하나만 사용하면 된다.


이터레이터는 외부 데이터를 다루기 위한 수단으로도 많이 사용된다.

이미지 분석을 위해 특정 디렉토리에 있는 이미지를 불러와서 미리 작성해둔 코드를 돌려가며 처리를 한다고 생각해보자. 배열을 사용하면 모든 이미지를 미리 불러다 놓아야 하기 때문에 메모리를 많이 소모할 것이다. 하지만, 이터레이터를 사용한다면 현재 다루고 있는 이미지 하나만 메모리에 올리면 된다.


이터레이터를 직접 만들어서 구현해야할까?

아니다! 제너레이터(generator) 함수를 이용하면 편리하게 이너레이터를 만들 수 있다!




▶ 📎참고

profile
블로그 이전했습니다!

0개의 댓글