Posts 모던 자바스크립트 #5.6. iterator
Post
Cancel

모던 자바스크립트 #5.6. iterator

모던 JavaScript 튜토리얼을 따라가면서 정리합니다.

for..of을 사용할 수 있는 객체를 이터러블이라고 부른다. 배열뿐만 아니라 문자열 등 다수의 내장 객체가 이터러블이다.

  • 이터러블엔 메서드 Symbol.iterator가 반드시 구현되어 있어야 한다.

    • obj[Symbol.iterator]의 결과는 이터레이터라고 부른다. 이터레이터는 이어지는 반복 과정을 처리한다.
    • 이터레이터엔 객체 {done: Boolean, value: any}을 반환하는 메서드 next()가 반드시 구현되어 있어야 한다. 여기서 done:true는 반복이 끝났음을 의미하고 그렇지 않은 경우엔 value가 다음 값이 된다.
  • 이터러블 객체 만들어보기

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

    range 를 이터레이터로 만들려면 객체에 Symbol.iterator라는 메서드를 추가한다. 그럼 다음과 같은 일이 벌어진다.

    • for..of가 시작되지 마자 for..ofSymbol.iterator를 호출한다. 없다면 에러가 발생한다. Symbol.iterator는 반드시 iterator(메서드 next가 있는 객체) 객체를 반환해야 한다.
    • 이후 for..of반환된 객체만을 대상으로 동작한다.
    • for..of에 다음 값이 필요하면, for..of는 이터레이터의 next() 메서드를 호출한다.
    • next()의 반환값은 {done: Boolean, value: any}와 같은 형태여야 한다. done=true는 반복이 종료되었음을 나타낸다. false일 땐 value에 다음 값이 저장된다.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    
    let 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 num of range) {
      alert(num); // 1, 2, 3, 4, 5
    }
    
  • 이터러블 객체의 핵심은 관심사의 분리(Seperation of concern, SoC)에 있다.

    • range에는 메서드 next()가 없다.
    • 대신 range[Symbol.iterator]()를 호출해서 만든 ‘이터레이터’ 객체와 이 객체의 메서드 next()에서 반복에 사용될 값을 만들어낸다.

    이렇게 하면 이터레이터 객체와 반복 대상인 객체를 분리할 수 있다.

  • 이터레이터 객체와 반복 대상인 객체를 합쳐서 객체 자체를 이터레이터로 만들면 더 간단하다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    
    let range = {
      from: 1,
      to: 5
      [Symbol.iterator]() {
      	this.current = this.from;
      	return this;
    	},
      
      next() {
        if (this.current <= this.last) {
          return { done: false, value: this.current++ };
        } else {
          return { done: true };
        }
       }
    };
      
    for (let num of range) {
      alert(num); // 1, 2, 3, 4, 5
    }
    

    이러면 range[Symbol.iterator]()는 객체 range 자체를 반환한다. 이 방식의 단점은 두 개의 for..of 반복문을 하나의 객체에 동시에 사용할 수 없다는 점이다. 이터레이터(객체 자신)가 하나뿐이어서 두 반복문이 반복 상태를 공유하기 때문이다.

  • 메서드 Symbol.iteratorfor..of에 의해 자동으로 호출되는데, 개발자가 명시적으로 호출하는 것도 가능하다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    let str = "Hello";
    // 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); // H, e, l, l, o
    }
    

    이런 경우는 거의 없지만, for..of보다 반복 과정을 더 잘 통제할 수 있다는 장점이 있다. 반복을 시작했다가 잠시 멈춰 다른 작업을 하다가 다시 반복을 시작하는 등 반복 과정을 여러개로 쪼갤 수 있다.

  • 문자열이나 배열 같은 내장 이터러블에도 Symbol.iterator가 구현되어 있다.

  • 문자열 이터레이터는 서로게이트 쌍을 지원한다.

  • 인덱스와 length 프로퍼티가 있는 객체는 유사 배열이라고 한다. 유사 배열 객체에는 다양한 프로퍼티와 메서드가 있을 수 있는데, 배열 내장 메서드는 없다.

  • 이터러블 객체라고 해서 유사 배열 객체는 아니다. 유사 배열 객체라고 해서 이터러블 객체인 것도 아니다. range는 이터러블 객체이지만 인덱스도 없고 length 프로퍼티도 없으므로 유사 배열 객체가 아니다.

    1
    2
    3
    4
    5
    6
    7
    8
    
    let arrayLike = {
      0: "Hello",
      1: "World",
      length: 2
    }
      
    // Symbol.iterator가 없으므로 에러 발생
    for (let item of arrayLike) {}
    

    arrayLike는 유사 배열이지만 이터러블 객체가 아니다.

  • 대부분의 메서드는 ‘진짜’ 배열이 아닌 이터러블이나 유사 배열을 대상으로 동작한다고 쓰여있는 걸 명세서에서 볼 수 있다. 이 방법이 더 추상적이기 때문이다.

  • Array.from(obj[, mapFn, thisArg])을 사용하면 이터러블이나 유사 배열인 obj를 진짜 Array로 만들 수 있다. 이렇게 하면 obj에도 배열 메서드를 사용할 수 있다. 선택 인수 mapFnthisArg는 각 요소에 함수를 적용할 수 있게 해준다.

    1
    2
    3
    
    let arr = Array.from(range, num => num * num);
      
    alert(arr); // 1, 4, 9, 16, 25
    
This post is licensed under CC BY 4.0 by the author.

이것이 안드로이드다 with 코틀린 #1 코틀린 기본문법: 7. 클래스와 설계

HIWORK: Column 'regiDate' cannot be null

Loading comments from Disqus ...