디버깅 중 겪은 처음보는 현상이 궁금하여 찾아본 내용을 정리


Array.from()

기본 사용 및 개념

array-like 또는 Iterable Object를 Array 인스턴스로 생성

  1. 문자열은 실제 배열은 아니지만, length를 포함하고 있기에 array-like로서 사용이 가능하다.
      const str = 'EHyang';
      const strArr = Array.from(str);
      console.log(strArr); // [ 'E', 'H', 'y', 'a', 'n', 'g' ]
    
  2. Index와 value를 포함하고, length를 가진 유사 배열 객체도 배열로 변환이 가능하다.
      const arrLike = { 0: 'H', 1: 'e', 2: 'l', 3: 'l', 4: 'o', length: 5 };
      const arrLikeToArr = Array.from(arrLike);
      console.log(arrLikeToArr); // [ 'H', 'e', 'l', 'l', 'o' ]
    
  3. Iterable 객체도 배열로 변환이 가능하다.
      const set = new Set(["foo", "bar", "ohh"]);
      const setArr = Array.from(set);
      console.log(setArr) // [ "foo", "bar", "ohh" ]
    

해맸던 부분

Array.from()을 추가하니 동작을 안한다?!

  겪었던 코드와 유사한 형태의 코드이다.

  const arr: Array<any> = [
    { id: 1, name: 'kim' },
    { id: 2, name: 'hong' }
  ];

  const arrEntries = arr.entries();

  // ...

  console.debug('debug -- ', Array.from(arrEntries));

  // ...

  for (const [idx, val] of arrEntries) {
    // logic..
  }

다음 코드에서 debug를 위해 로그를 추가하니 아래 for문이 돌지 않았다.

처음에는 Array.from() 메소드가 arrEntries를 얕은 복사 후, arr로 변환하면서 하나씩 빼서 사용하고, 빈값만 남겨둬 for문에서 동작하지 않는줄 알았다.

이것은 반은 맞고 반은 틀린 얘기였다.

  const arrEntries = arr.entries(); // Array Iterator Object

entries() 메소드는 배열의 각 인덱스에 대한 키-값 쌍을 포함하는 Iterator 객체를 반환한다.

Iterator는 상태값을 가진 객체여서, 한번 순회를 하게되면 상태가 변경되고, 해당 Iterator는 재사용이 불가해진다.

  // ...
  const arrEntries = arr.entries();

  while(true) {
    const n = arrEntries.next();
    console.log(n);
    if (next.done) {
      break;
    }
  }

  // 출력 0: { value: [ 0, { id: 1, name: 'kim' } ], done: false }
  // 출력 1: { value: [ 1, { id: 2, name: 'hong' } ], done: false }
  // 출력 2: { value: undefined, done: true }

다음과 같이 Iterator를 소비해보며 로그를 찍어보면, done이라는 상태값을 볼 수 있다.

Array.from()에서 이미 Iterator를 소비했으니, 다음 for문은 빈값만 바라 보고 있을 수 밖에…

다소 허무한 결말이였지만, 지식 +1 🧑🏻‍🎓 🤦🏻‍♂️

과거 C++ 로 코딩테스트 준비하면서 Iterator 개념을 많이 익혔었는데, 잊고 살다가 다시 만나게 되니 많이 반갑다.

얼떨결에 써버린 첫 포스팅, 글재주가 많이 없지만 점차 나아지기를 바라며 이만 마친다.

참고