1.1 객체지향 디자인 패턴의 반복자 패턴과 일급 함수

멀티패러다임 언어에서는 함수형 패러다임을 적용하는 방법으로 반복자 패턴을 활용

1.1.1 GoF의 반복자 패턴

객체지향 디자인 패턴중 하나. 컬렉션의 요소를 순차적으로 접근하는 규약

반복자(iterator)의 구조를 타입스크립트 인터페이스 정의를 사용해 표현

// iterator가 아직 완료되지 않았음
interface IteratorYieldResult<T> {
    done?: false;
    value: T;
}

// iterator가 완료되었음
interface IteratorReturnResult {
    done: true;
    value: undefined;
}

// next() 매서드를 가진 인터페이스. 위의 인터페이스 둘 중 하나를 반환한다.
interface Iterator<T> {
    next(): IteratorYieldResult<T> | IteratorReturnResult;
}

실제 Iterator 인터페이스는 lib.es2015.iterable.d.ts를 참고하면 된다.

1.1.2 ArrayLike로부터 Iterator 생성하기

반복자 패턴을 시작으로 여러 객체지향 언어들이 함수형 패러다임을 스스로 구현한 과정에 집중!

/**
 * lib.es5.ts
interface ArrayLike<T> {
    readonly length: number;
    readonly [n: number]: T;
}
*/

class ArrayLikeIterator<T> implements Iterator<T> {
    private index = 0;
    constructor(private arrayLike: ArrayLike<T>) {}

    next(): IterableResult<T> {
        if (this.index < this.arrayLike.length) {
            return {
                value: this.arrayLike[this.index],
                done: false
            }
        } else {
            return {
                value: undefined,
                done: true
            }
        }
    }
}

실제로 사용해 보자

const arrayLike: ArrayLike<number> = {
    0: 10,
    1: 20,
    2: 30,
    length: 3
}

const iterator: Iterator<number> = new ArrayLikeIterator(arrayLike)

console.log(iterator.next()) // { value: 10, done: false }
console.log(iterator.next()) // { value: 20, done: false }
console.log(iterator.next()) // { value: 30, done: false }
console.log(iterator.next()) // { value: undefined, done: true }

ArrayLikeIterator 클래스는 GoF의 반복자 패턴을 따르고 있음.

ArrayLike는 0부터 시작하는 number 키와 length 속성을 가진 객체이다.

자바스크립트에는 ArrayLike 타입의 값들이 많다. Array, arguments, NodeList 등. ArrayLikeIterator를 사용하면 이런 ArrayLike 객체들도 마치 Array처럼 다양한 컬렉션을 순회할 수 있게끔 만들 수 있다.

const array: Array<string> = ['a', 'b', 'c']
const iterator = new ArrayLikeIterator(array)

console.log(iterator.next()) // { value: 'a', done: false }
console.log(iterator.next()) // { value: 'b', done: false }
console.log(iterator.next()) // { value: 'c', done: false }
console.log(iterator.next()) // { value: undefined, done: true }