Home iterator
Post
Cancel

iterator

Iterator

이터레이터를 알기 전에 용어를 간단하게 정리하고 가자

iterator

next() 메서드를 가지고 있는 객체로 [Symbol.iterator]에 의해 생성된다.

next()메서드는 반드시 {done: boolean [, value: any]} 꼴의 객체를 반환해야한다.

iterable

반복 가능한 이라는 뜻을 나타내며 iterator객체를 반환할 수 있는 [Symbol.iterator] 를 가지고 있는 객체를 iterable객체라고 한다.

자바스크립트 내장 iterable객체는 다음과 같다.

자바스크립트 내장 iterable객체는 다음과 같다.

  1. Array
  2. Set
  3. Map
  4. DOM NodeList
  5. primitive string

정리해보면 위의 다섯 친구들은 [Symbol.iterator]메서드를 호출할 수 있고, [Symbol.iterator]메서드를 호출하면 next()메서드를 가지고 있는 iterator객체가 반환된다.

1
2
3
4
5
const arr = [1,2,3];
const iterator = arr[Symbol.iterator]();

console.log(iterator);

iterator

for…of

눈치 챈 사람도 있겠지만 위에서 언급한 반복가능한 객체들은 for...of 으로 순회가 가능하다. 아무 생각 없이 써왔던 for...of는 iterable 객체만 사용이 가능한 것이었다.

for...of가 어떻게 iterable객체를 순회하는지 과정을 살펴보자

  1. for...of가 시작되자마자 for...of는 iterable객체에서 [Symbol.iterator]를 호출한다.
  2. 이후 for...of는 1번에서 호출한 [Symbol.iterator]의 반환 객체인 iterator객체로 동작한다.
  3. for...of은 iterator객체의 next()메서드를 호출하고 {done: false [, value: any]}를 반환받는다
  4. next()메서드의 반환값이 {done: true}일 때까지 반복한다.

동작 과정만 살펴보면 for...of은 내장 iterable객체가 아니더라도 [Symbol.iterator]메서드만 가지고 있으면 작동할 것 같다.

사실 [Symbol.iterator]를 가지고 있으면 이미 iterable객체이긴 하지만 중요한 것은 iterable 하지 않은 객체를 iterable하게 만들 수 있다는 것

iterable하게 만들기

1
2
3
4
5
6
7
8
9
10
11
const obj = {
  a: 1,
  b: 2,
  c: 3,
  d: 4,
};

for(let v of obj){
  console.log(v);
}
// Uncaught TypeError: obj is not iterable

obj는 iterable하지 않기 때문에 당연히 for...of를 쓸 수 없다.

우리가 obj에 직접 [Symbol.iterator]를 생성해준다면 어떨까?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const range = {
  from: 1,
  to: 5,
};

range[Symbol.iterator] = function (){
  return {
    current: this.from,
    last: this.to,

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

for(let v of range){
  console.log(v); // 1, 2, 3, 4, 5
}

from부터 to까지 순차적으로 증가하는 iterator를 반환하는 [Symbol.iterator]메서드를 생성해주면 for...of가 잘 돌아가는 것을 확인할 수 있다.

서로게이트 페어

자바스크립트로 문자열을 역순으로 뒤집다보면 다음과 같은 상황을 마주할 수 있다.

1
2
3
4
const str = '𝒳😂'; 
const reversedStr = str.split('').reverse();

console.log(reversedStr); // �💳�

UTF-16로 표현할 수 없는 문자들을 두 개의 16비트 한 쌍으로 표현하는 써로게이트 페어 문자를 마주할 경우 이런 일이 발생한다.

1
2
'𝒳' === '\uD835\uDCB3'
'😂' === '\uD83D\uDE02'

사실은 이런식으로 표현되어있기 때문에 split을 사용해서 뒤집을 경우 16비트 단위로 잘려 원래 페어를 찾을 수 없기 때문에 형태를 유지하지 못하고 괴문자가 나오게되는 것

이 경우 쉽게 문자열을 뒤집을 수 있는 방법이 존재하는데 Array.from()을 이용하는 것이다.

Array.from()

1
2
3
4
const str = '𝒳😂';
const reversedStr = Array.from(str).reverse().join('');

console.log(reversedStr); // 😂𝒳

Array.from은 16비트 단위로 끊어서 배열을 만드는 것이 아니라 유사 배열 객체와 iterable 객체를 진짜 배열로 만들어준다.

문자열은 iterable 객체이기 때문에 iterator를 이용해 문자열을 잘라서 배열을 만드는 것이 아닌 next()가 반환하는 값으로 배열을 만들기 때문에 정상적인 문자열 뒤집기가 가능하다.

split 메서드에 비해 속도는 느리다고 하니 참고.

유사 배열 객체

인덱스와 length 프로퍼티가 있는 객체를 유사 배열 객체라고 부른다.

왜 인덱스와 length까지 있는 따로 추상화시킨 것일까? 그냥 배열을 사용하면 안되는 걸까?

스택오버플로우에 나와 같은 궁금증을 가진 사람이 존재하긴했지만.. 딱히 시원한 대답을 얻지는 못했다

개인적인 생각으로는 유사 배열은 배열에 있는 메서드를 따로 사용할 필요가 없어서 인 것 같다.

대표적인 유사 배열에는 arguments나 querySelectorAll로 가져온 nodes등이 있는데 이 친구들은 데이터를 그냥 담은 컨테이너 자체일 뿐이다. 이를 배열로 표현한다면 불필요한 push나 pop 등등의 메서드들이 들어가게 되는데 이를 방지하기 위함이 아닌가..싶다.


Javascript의 Iterator와 Generator

iterable 객체

This post is licensed under CC BY 4.0 by the author.