const reduce = (f, acc, iter) => { if (!iter) { iter = acc[Symbol.iterator](); acc = iter.next().value; } // parameter가 2 개만 들어왔을 경우 iter가 없는 경우이다. // 이 때는 자기 자신을 반환하는 iterable의 성질을 이용해서 iter를 만들어준다. for (const a of iter) { acc = f(acc, a); } return acc; };
const map = (f, iter) => { let res = []; for (const p of iter) { res.push(f(p)); } return res; }; console.log(document.querySelectorAll("*").map((ele) => ele.nodeName)); // 배열이 아니기 때문에 에러가 발생한다. console.log(map((el) => el.nodeName, document.querySelectorAll("*"))); // nodeList는 이터러블 프로토콜을 따르기 때문에 동작한다.
function* oods(l) { for (let i = 0; i < l; i++) { if (i % 2) yield i; } } let iter2 = oods(6); console.log(iter2.next()); // {value:1,done:false} console.log(iter2.next()); // {value:3,done:false} console.log(iter2.next()); // {value:5,done:false} console.log(iter2.next()); // {value:undefined,done:true}
또한 generator는 while을 이용하여 작성할 수 있다.
아래의 infinity는 무한수열을 만드는 generator이고 limit 함수는 l까지는 a를 yield하다가 l이 될 경우 return 하는 generator이다. infinity generator와 limit generator를 이용하여 odds generator도 새로 정의하였다.
// 배열 순회 const list = [1, 2, 3]; for (var i = 0; i < list.length; i++) { console.log(list[i]); } // 유사 배열 순회 const str = "abc"; for (var i = 0; i < str.length; i++) { console.log(str[i]); }
// 1 // 2 // 3 // a // b // c
for of
ES6에선 for of문이 추가되어 좀 더 명령형에서 선언형으로 바뀌었다. 인덱스로 직접 접근할 수 없는 리스트도 순회할 수 있다.
IteratorResult 객체는 done: boolean과 value: any 프로퍼티를 - 가진다.
이전 next 메서드 호출의 결과로 done 값이 true를 리턴했다면, 이후 호출에 대한 done 값도 true여야 한다.
Well-formed iterable
Iterator면서 Iterable인 객체를 Well-formed iterable이라고 한다. 즉 이터레이터의 이터러블이 자기 자신을 반환하면 Well-formed iterable이다.
Iterable/Iterator 프로토콜
Iterable을 for…of, 전개 연산자 등과 함께 동작하도록 한 규약이다.
for of의 동작원리
for of는 Iterable 객체에 사용할 수 있다.
for of를 호출하게 되면 Symbol.iterator를 통해 Iterator를 반환한다.
즉 for of는 Iterable/Iterator 프로토콜에 따라 동작한다.
1 2 3 4 5 6 7
const list = [1, 2, 3]; for (const a of list) { // for of는 Iterable/Iterator 프로토콜에 따라 동작한다. // 즉 반환된 Iterator의 value를 a로 참조한다. // done이 true가 되면 반복문을 빠져나온다. console.log(a); }
따라서 코드를 다음과 같이 수정할 수 있다.
1 2 3 4 5 6 7
let list = [1, 2, 3]; let iterator = list[Symbol.iterator](); while (1) { const { value, done } = iterator.next(); if (done === true) break; console.log(value); }
const map = newMap([ ["a", 1], ["b", 2], ["c", 3], ]); for (const a of map.keys()) console.log(a); // Map,Set의 key를 value로 하는 Iterator를 반환하는 메서드이다. // a // b // c for (const a of map.values()) console.log(a); // Map,Set의 value를 value로 하는 Iterator를 반환하는 메서드이다. // 1 // 2 // 3 for (const a of map.entries()) console.log(a); // Map,Set 전체를 value로 하는 Iterator를 반환하는 메서드이다. // ['a',1] // ['1',2] // ['1',3]
const a=[1,2=]; console.log([...a,...[3,4]]) ///[1,2,3,4]; a[Symbol.iterator]=null; console.log([...a,...[3,4]]) // Uncaught TypeError: a is not iterable
Iterable 프로토콜을 따르고 있기 때문에 Map,Set과 같은 여러 Iterable객체에 적용된다.