본문 바로가기
WEB/깊게 공부하기

[JS] iterable, iterator 살펴보기 - Generator 1

by IT황구 2023. 10. 16.
728x90
반응형

안녕하세요!

 

오랜만에 기술 포스트를 작성하네요. 이번 글은 시리즈처럼 작성될 예정입니다. 

 

babel이 Generator를 transpile한 코드를 이해하고, 어떻게 작동하는지 이해하기 위해 필요한 지식들을 미리 정리합니다.

 

 

개요

JS에서 spread operator(...) 또는 for...of를 통해서 배열, 문자열 등을 펼치고 순회할 수 있습니다.

Map, Set도 순회가 가능하다는데 이걸 다 암기하거나 매번 실험 할까요?? 이렇게 해도 되긴 합니다.

 

하지만 브라우저가 어떻게 순회 가능하다고 판단하는지 이해한다면, 암기하지 않아도 되고 다른 코드를 이해할때 큰 도움이 됩니다.

 

Iterable, Iterator protocol

iterable, iterator protocol 자체는 브라우저에 내장되어 있는 어떤 문법 같은것이 아닙니다.

특정 규칙을 만족한다면 어디서나 따를 수 있는 것 입니다.

 

protocol을 잘 이용하면 내가 만든 커스텀한 object를 순회 할 수 있게 만들고, 어떻게 동작할지 정의할 수 있습니다.

우리가 무의식적으로 사용하던 내용들이 실제로 이런 규칙을 따랐기에 순회할 수 있었던 것입니다.

 

자세히 알아보겠습니다.

 

iterable

iterable이란  spread operator 또는 for...of를 통해서 js object(Array, Map)등을 순회할 수 있게 만들어주는 프로토콜 입니다.

 

규칙은 굉장히 간단합니다.

 

iterable이 되기 위해선

- `[Symbol.iterator]()` 를 정의하면 됩니다. (@@iterator method)를 구현해야 합니다.

- `[Symbol.iterator]()`는 iterator protocol을 만족하는 객체를 반환해야 합니다.

 

만약 iterable protocol을 구현하지 않고 순회한다면 어떻게 될까요? 

에러에 정확히 이유를 표시해줍니다. customObj는 `iterable`이 아니라고 합니다.

const customObj = {
  count : 0
}

for(const val of customObj) {
  console.log(val);
}

 

iterable protocol을 만족하기 위해 `[Symbol.iterator]()` 를 추가합니다.

 

const customObj = {
    count : 0,
    [Symbol.iterator](){
        console.log('iterator called')
        return {};
    },
}
  
for(const val of customObj) {
    console.log(val);
}

이렇게 만드니 for...of가 실행되고 `@@iterator`가 실행된 것을 확인할 수 있습니다.

하지만 `undefined is not a function` 이 반환됩니다. 뭐가 문제일까요?

 

이건 for...of가 동작하는 방식 때문입니다.

1. [Symbol.iterator]() 을 호출한다. 여기서 iterator를 반환 받는다

2. iterator의 `next()` method를 호출한다.

3. `next()` 에서 반환된 값 { value : xx, done : true or false } 를 사용해서 값을 출력한다.

저는 위의 방식 중 2번 `next()`를 구현하지 않았습니다.

따라서 iterator.next() 호출시에 undefined를 호출한다고 나오는 것입니다.

 

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of#description

 

for...of - JavaScript | MDN

The for...of statement executes a loop that operates on a sequence of values sourced from an iterable object. Iterable objects include instances of built-ins such as Array, String, TypedArray, Map, Set, NodeList (and other DOM collections), as well as the

developer.mozilla.org

 

여기서 `next()` 함수는 무엇이고? `{value: xx, done: xx}` 형태의 객체는 무엇이지? 할 수 있습니다.

바로 이게 iterator protocol 입니다.

 

iterator

iterator protocol은 일련의 값들을 만들어내는 표준 방법을 정의합니다. 말이 너무 어렵습니다.

 

iterator protocol을 준수하려면 어떻게 해야할까요?

 

1. next() method를 구현해야 합니다. (필수)

- 참고: generator에서 사용되는 return(), throw() 같은 함수들도 있습니다. 이건 필수가 아닙니다.

2. next()는 `IteratorResult` interface를 만족하는 객체를 반환해야 합니다. ex) { done: true, value: 3 }

https://tc39.es/ecma262/multipage/control-abstraction-objects.html#sec-iteratorresult-interface

IteratorResult Interface가 정의된 스펙문서를 확인해보면 Property 2개가 필요하다고 나옵니다. 'done', 'value'

 

done과 value 값은 optional 입니다. optional인 이유는 초기값이 다 있기 때문입니다.

아무것도 없다면 { done: false, value: undefined } 와 같습니다.

 

형태를 보니 generator와 뭔가 비슷하죠?? 다 이어지는 내용입니다.

 

이제 위에서 봤던 예제를 이어서 구현해 보겠습니다.

 

iterator protocol을 준수하기 위해 next()를 구현하고, IteratorResult 형태로 객체를 반환하게 했습니다.

const customObj = {
    [Symbol.iterator]() {
        console.log("iterator 반환")
        let count = 0;
        return {
            next() {
                if(count > 3) {
                    return { done: true, value: count++};
                }

                return { done: false, value: count++ };
            }
        }
    }
}
  
for(const val of customObj) {
    console.log(val);
}

console.log([...customObj]) // [0, 1, 2, 3]

이제 정상적으로 작동하는 것을 확인할 수 있습니다. 또한 spread operator도 잘 작동합니다.

 

지금까지 설명한 내용에서 알 수 있는것은 꼭 Array나 Map만 가능한것이 아니라는 것 입니다.

특정 규칙을 만족하면 순회 가능한 객체를 생성할 수 있습니다.

 

어디에서 사용되고 있을까?

VSCode를 구현한 코드를 보면 linked-list에서 직접 iterable protocol을 준수해서 작업한 것을 확인할 수 있습니다.

또한 generator의 polyfill 또한 직접 [Symbol.iterator]()를 등록합니다.

 

알고나면 코드를 이해할 수 있고, 응용이 가능해집니다.

 

vscode에서 linkedList에 iterable을 구현한 예시

https://github.com/microsoft/vscode/blob/main/src/vs/base/common/linkedList.ts#L135-L142

 

 

Built-in iterables

Array, Map 같은 객체들은 어떻게 아무것도 구현하지 않아도 순회가 되는 것일까요?

각 객체들에 built-in iterable이 있기 때문입니다. 

 

프로토타입 체인을 이용해 Iterator 공통 클래스에 구현된 내용들을 사용할 수 있습니다.

- `@@iterator` 을 이용해서 iterable을 만족

- next() 같은 iterator protocol에 필요한 메소드도 지원

 

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator/@@iterator

 

어떻게 확인할 수 있을까요? `string`에 Symbol.iterator가 있는지 확인해 보겠습니다.

 

"123"의 wrapper object인 String.prototype에 [Symbol.iterator]가 존재합니다. 다음의 예시로 알 수 있습니다.

 

위의 사진과 같이 Object.prototype에서 [Symbol.iterator]에 접근하면 에러가 나는것을 확인할 수 있습니다.

 

또한 prototype에 `next` 함수도 구현되어 있는것을 확인할 수 있습니다.

 

 

Map도 다음과 같이 iterable, iterator protocol을 모두 준수하는것을 알 수 있습니다.

 

 

정리

정리를 하다보니 JS 개념서의 목차에 왜 iterable, generator와 같은 내용이 묶여있는지 이해 되었습니다.

 

이번 글에서는 크게 4가지를 알 수 있습니다.

 

1. iterator, iterable protocol이 무엇이고, 어떻게 구현하는지?

2. for..of, spread opreator에서 작동하는 custom iterator, iterable 만들어보기

3. iterable을 직접 구현해서 사용하는 실제 예시 (vscode)

4. Map, Array가 직접 구현하지 않고 순회할 수 있는 이유 및 확인 방법

 

이 외에도 spec 문서에서 iterable protocol에 대해서 확인하고, iterator protocol의 반환값을 통해 generator와의 연관성도 알 수 있었습니다.

 

그리고 아직 나오진 않았지만 custom iterator를 구현할때도 구현 방법에 따라서 완전히 다른 결과가 나오게 됩니다.

 

다음 내용에는 iterable iterator protocol에 대해서도 알아보겠습니다.

 

 

감사합니다!

 

Reference

- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols

 


https://rbals0445.tistory.com/143

 

[JS] generator 살펴보기 - Generator 2

Generator 1편과 이어서 보신다면 더 좋습니다. https://rbals0445.tistory.com/142 [JS] iterable, iterator 살펴보기 - Generator 1 안녕하세요! 오랜만에 기술 포스트를 작성하네요. 이번 글은 시리즈처럼 작성될 예

rbals0445.tistory.com

 

 

 

728x90
반응형