for...of, ...(spread), Array.from() 등의 일부 메서드들은 특정 값에서는 될 때가 있고, 안 될 때가 있습니다.
이런 메서드들은 내부에서 반복이 가능한지 먼저 판단하는 규칙(프로토콜)을 따라 메서드 사용 가능 여부가 결정됩니다.
for (const x of [1,2,3]){} // 가능
for (const x of {a: 1, b:2}) {} // TypeError
위와 같은 차이를 이해하기 위해서는 iterable과 iterator를 구분할 수 있어야 됩니다.
Iterable은 책(순회 구조의 대상)
Iterator는 페이지를 넘기는 손(다음 값을 꺼내주는 역할)
결국 Iterable*은 순회 가능한 대상, Iterator는 순회 작업을 하는 객체**입니다.
특정 객체가 아래의 조건을 만족하면 iterable한 것입니다.
obj [Symbol.iterator]가 함수여야 함const arr = [1, 2, 3];
console.log(typeof arr[Symbol.iterator]); // 'function'
iterator는 아래의 조건을 만족합니다.
next() 메서드를 가지고 있음 next()는 매번 {value, done}형태의 객체를 반환done:false면 값이 있음done:true면 반복을 종료const it = [1, 2].values();
console.log(it.next()); // { value: 1, done: false }
console.log(it.next()); // { value: 2, done: false }
console.log(it.next()); // { value: undefined, done: true }
아래의 타입을 가진 데이터들은 for...of, ...(배열, 인자 spread), Array.from()사용 가능합니다.
console.log([...new Set([1, 1, 2])]); // [1, 2]
console.log(Array.from("abc")); // ['a', 'b', 'c']
Object(일반 객체)는 기본적으로 [Symbol.iterator]를 제공하지 않기 때문에 iterable이 아닙니다.
const obj = { a: 1};
console.log(obj[Symbol.iterator]); // undefined
그래서 반복하려면 명시적으로 iterable로 변환을 해줘야 됩니다.
Object.keys(obj) → 키 배열Object.values(obj) → 값 배열Object.entries(obj) → 배열const obj = { a: 1, b: 2 };
for (const k of Object.keys(obj)) console.log(k);
for (const v of Object.values(obj)) console.log(v);
for (const [k, v] of Object.entries(obj)) console.log(k, v);