가. 기존과 달라진 ES6에서의 리스트 순회

나. Array, Set, Map을 통해 알아보는 이터러블 / 이터레이터 프로토콜

다. 사용자 정의 이터러블/이터레이터 프로토콜 정의

사용자 정의 이터러블을 구현하면서 이터레이터에 대한 이해도를 높여보자.

// 사용자 정의 이터러블이 wellFormed iterator를 반환할 수 있도록 하기 위해서는
// 자기자신 또한 iterator면서 자기 자신을 리턴하도록 해주어야한다. (return this)
// 그래야 중간에 for of문에 들어가더라도 이전까지 진행되었던 자기의 상태에서 계속해서 
// next()를 할 수 있도록 만들어두어야 wellformed iterator인 것이다.
const iterable = {
	[Symbol.iterator](){
		let i = 3;
		return {
			next(){
				return i === 0 ? { done: true } : { value : i--, done:false } 
			},
			[Symbol.iterator](){ return this; } // 자기 자신을 리턴하도록 해서 어디에서든 진행상태를 기억하고 next 할 수 있도록 함
		}
	}
};
let iterator = iterable[Symbol.iterator]();
// log(iterator.next()); // {value: 3, done: false}
// log(iterator.next()); // {value: 2, done: false}
// log(iterator.next()); // {value: 1, done: false}
// log(iterator.next()); // {done: true}

for(const a of iterator) log(a); // 3, 2, 1
const arr2 = [1, 2, 3];
let iter2 = arr2[Symbol.iterator]();
// iter2.next(); // {value: 1, done: false}
log(iter2[Symbol.iterator]() === iter2); // true
// Iterator가 자기 자신을 반환하는 Symbol.iterator 메서드를 가지고 있을 때 
// wellFormed iterator, wellFormed Iterable라고 할 수 있다.
for(const a of iter2) log(a); // 2, 3
for (const a of document.querySelectorAll('*')) log(a);
const all = document.querySelectorAll('*');
let iter3 = all[Symbol.iterator]();
log(iter3.next());
log(iter3.next());
log(iter3.next());

라. 전개 연산자

전개연산자도 마찬가지로 이터러블/이터레이터 프로토콜 규약을 따른다.

const a = [1, 2]; // array iterable
const arr = [1, 2, 3];
const set = new Set([1, 2, 3]);
const map = new Map([['a', 1], ['b', 2], ['c', 3]]);

// a[Symbol.iterator] = null; // a is not iterable
log([...a, ...[3, 4]]); // [1, 2, 3, 4]
log([...a, ...arr, ...set, ...map.keys()]); // [1, 2, 1, 2, 3, 1, 2, 3, "a", "b", "c"]