자료구조와 자료형 : iterable 객체

라용·2022년 12월 19일
0

모던 JavaScript 튜토리얼 내용 중 일부를 정리한 내용입니다.

interable 은 반복 가능한의 의미로, iterable 객체는 배열을 일반화한 객체입니다. 배열은 대표적인 이터러블이며 그 외에 문자열 역시 이터러블입니다. 배열이 아닌 객체에 배열처럼 for .. of 문법을 적용할 수 있는 유용한 방법을 소개합니다.

아래와 같은 객체가 있을 때,

let range = {
  from: 1,
  to: 5
};

// 아래와 같이 동작하도록 하려면,
// for(let num of range) ... num=1,2,3,4,5

range 를 이터러블로 만들려면 객체에 Symbol.iterator 라는 메서드를 추가해야 합니다. for .. of 가 시작되면 Symbol.iterator 를 호출해 반환된 객체, 이터레이터를 대상으로 동작하는데 이때 이터레이터의 next() 메서드르 호출합니다. next() 의 반환 값은 {done : Boolean, value: any} 와 같은 형태이고 done = true 는 반복이 종료되었음을 의미하고 done = false 는 value 에 값이 저장됩니다. 아래 코드를 참고하세요.

let range = {
  from: 1,
  to: 5
};

// for..of 최초 호출 시, Symbol.iterator가 호출
range[Symbol.iterator] = function() {

  // Symbol.iterator는 이터레이터 객체를 반환
  // 이후 for..of는 반환된 이터레이터 객체만을 대상으로 동작하는데, 이때 다음 값도 정해짐
  return {
    current: this.from,
    last: this.to,

    // for..of 반복문에 의해 반복마다 next()가 호출
    next() {
      // next()는 값을 객체 {done:.., value :...}형태로 반환
      if (this.current <= this.last) {
        return { done: false, value: this.current++ };
      } else {
        return { done: true };
      }
    }
  };
};

for (let num of range) {
  alert(num); // 1, then 2, 3, 4, 5
}

이터러블 객체의 핵심은 관심사 분리입니다. 이터레이터 객체와 반복 대상 객체를 합쳐서 range 자체를 이터레이터로 만들면, 코드는 아래처럼 간단해집니다.

let range = {
  from: 1,
  to: 5,

  [Symbol.iterator]() {
    this.current = this.from;
    return this;
  },

  next() {
    if (this.current <= this.to) {
      return { done: false, value: this.current++ };
    } else {
      return { done: true };
    }
  }
};

for (let num of range) {
  alert(num); // 1, then 2, 3, 4, 5
}

for .. of 로 문자열을 바로 순회할 수 있지만, 아래처럼 이터레이터를 만들고 값을 수동으로 가져오면 반복과정을 더 잘 통제할 수 있습니다. 반복을 시작했다 멈추고 다른 작업을 하다가 다시 반복하는 과정을 쪼개는 것이 가능합니다.

let str = "Hello";

// for..of를 사용한 것과 동일한 작업을 함
// for (let char of str) alert(char);

let iterator = str[Symbol.iterator]();

while (true) {
  let result = iterator.next();
  if (result.done) break;
  alert(result.value); // 글자 하나씩 출력
}

이터러블과 유사 배열은 비슷하지만 다릅니다. 이터러블은 위에서 설명한 것처럼 메서드 Symbol.iterator 가 구현된 객체이고 유사 배열은 인덱스와 length 프로퍼티가 있어서 배열처럼 보이는 객체입니다. 브라우저 환경에서 이터러블 객체나 유사 배열 객체 혹은 둘 다인 객체를 만날 수 있는데, 문자열이 둘 다 속하는 대표적인 예입니다. 이터러블과 유사 배열은 대게 배열이 아니라서 push, pop 등 메서드를 지원하지 않습니다. 이 때 Array.from 을 사용하면 이를 진짜 배열로 만들어 배열 메서드를 사용할 수 있습니다.

let arrayLike = {
  0: "Hello",
  1: "World",
  length: 2
};

let arr = Array.from(arrayLike); // (*)
alert(arr.pop()); // World (메서드가 제대로 동작합니다.)

Array.from 엔 매핑 함수를 선택적으로 넘겨줄 수 있습니다.

Array.from(obj[, mapFn, thisArg])

// range는 챕터 위쪽 예시에서 그대로 가져왔다고 가정

// 각 숫자를 제곱
let arr = Array.from(range, num => num * num);
alert(arr); // 1,4,9,16,25

문자열을 배열로 만들면,

let str = '𝒳😂';

// str를 분해해 글자가 담긴 배열로 만듦
let chars = Array.from(str);

alert(chars[0]); // 𝒳
alert(chars[1]); // 😂
alert(chars.length); // 2

// 기술적으로 아래와 같이 동작하는 것

let str = '𝒳😂';

let chars = []; // Array.from 내부에선 아래와 동일한 반복문이 돌아갑니다.
for (let char of str) {
  chars.push(char);
}

alert(chars);
profile
Today I Learned

0개의 댓글