직접 사용자 반복 처리기, iterator 반복처리기 구현해보기
iterator 객체는 반복을 위한 준비이다.
행위와 상태를 분리한 것이다.
이번엔 행위 쪽도 직접 작성해보자.
// 첫번째 인자값 : 이터러블 객체
// 두번째 인자값 : 이 이터러블 객체가 반복될 때마다 해야될 일을 함수 f로 받는다.
const loop = (iter, f) => {
// 우선 이터러블인지 아닌지를 검증
// Symbol.iterator가 function인지를 검사, 그리고 이 함수를 불러서 return 받는게 이터레이터 객체
// 이것이 이터러블의 정의
// 이전에는 이것이 동적 데이터를 함부로 검사한 것처럼 보였을 것이다.
// 하지만 지금은 자바스크립트 공식 인터페이스인 이터러블을 약식으로 검증했구나. 라고 보여야됨.
// 그리고 iter도 검증을 해줘야된다. iterator result object인지 검증 필요. 어떻게?
// in으로 value와 done이 있는지 검증해야된다. 여기까지는 귀찮아서 안함.
// 어쨌든 이는 단순히 런타임 값 검사가 아니라 자바스크립트 공식 인터페이스인지 아닌지를 검증하는 것으로 봐야된다.
if (typeof iter[Symbol.iterator] == 'function') {
// 이터러블 인터페이스를 통해서 이터레이터를 얻었다.
iter = iter[Symbol.iterator]();
}else return;
// iterator result object가 아니라면 건너뜀
if (typeof iter.next != 'function') return;
// 위 과정을 통과하면 드디어 루프를 돈다.
// 아래 루프처리기는 아무 일도 못함. 그냥 돌린다라는 행위말고는 아무 것도 못함.
// 그렇기 때문에 while 안에 true가 들어감. 무한루프를 돔.
// 기본적으로 아래 루프문은 반복말고는 다른걸 할 생각이 없는 것이다.
// 아래 while은 단순히 반복기일 뿐. 아무런 권한과 책임이 없다.
// 이건 재귀함수로 짜도 상관없다. next의 결과값 done이 true일 때까지만 도는 재귀함수로 짜도 상관 없다는 것.
do{
const v = iter.next();
if (v.done) return; // 종료처리
f(v.value); // done이 false라면 현재 값을 전달함
}while(true)
}
반복될 때의 조건에 해당하는 값들과, 반복기를 분리했더니
반복기 쪽에서는 그냥 돌리기만하면 되는, 즉, 책임이 확 줄고,
나머지 상태관리나 루프에 대한 책임은 모두 iterator 객체가 가져가 버렸다.
이런식으로 짜면 iterator 객체는 몇번이고 루프를 돌아도 안정적으로 성공할 것이다.
여러분은 더 이상 어려운 루프를 짜지 않아도 된다는 것이다.
const loop = (iter, f) => {
if (typeof iter[Symbol.iterator] == 'function') {
iter = iter[Symbol.iterator]();
}else return;
if (typeof iter.next != 'function') return;
do{
const v = iter.next();
if (v.done) return;
f(v.value);
}while(true)
}
const iter = {
arr: [1, 2, 3, 4],
[Symbol.iterator]() {return this;},
next() {
return {
done: this.arr.length == 0,
value: this.arr.pop()
}
}
};
loop(iter, console.log);
이것을 iterator pattern이라고 한다.
이런 iterator pattern을 구현하는데 있어서, 자바스크립트 표준 스펙이 나오고 이걸 구현하는 공식적인 방법이 스펙으로 정리되어있을 뿐이다.
여러분들이 여태 나름대로의 iterator를 구현해왔다면 앞으로는 자바스크립트 표준 스펙에 맞춰 iterator를 구현해야 된다.
그 이유는 언어의 혜택이 굉장히 많기 때문이다.