이터레이션 프로토콜
순회 가능한 데이터 컬렉션(자료구조)을 만들기 위해 ECMA 사양에 정의하여 약속한 규칙
- 이터러블 프로토콜
- 이터레이터 프로토콜
이터러블
이터러블 프로토콜을 준수한 객체
Symbol.iterator
를 프로퍼티 키로 사용한 메서드를 직접 구현하거나 프로토타입 체인을 통해 상속받은 객체const isIterable = v => v !== null && typeof v[Symbol.iterator] === 'function';
isIterable([]); // true
isIterable(''); // true
isIterable(new Map()); // true
isIterable(new Set()); // true
isIterable({}); // false
Symbol.iterator
메서드를 직접 구현하지 않거나 상속받지 않은 일반 객체는 이터러블 프로토콜을 준수한 이터러블이 아님for...of
문으로 순회 불가, 스프레드 문법과 배열 디스트럭처링 할당의 대상으로 사용 불가이터러블의 Symbol.iterator
메서드를 호출하면 이터레이터 프로토콜을 준수한 이터레이터 반환
이터러블의 Symbol.iterator
메서드가 반환한 이터레이터는 next
메서드를 가짐
이터레이터의 next
메서드는 이터러블의 각 요소를 순회하기 위한 포인터 역할
→ next
메서드를 호출하면 이터러블을 순차적으로 한 단계씩 순회하며 순회 결과를 나타내는 이터레이터 리절트 객체 반환
이터레이터 리절트 객체
value
프로퍼티 : 현재 순회 중인 이터러블의 값done
프로퍼티 : 이터러블의 순회 완료 여부const array = [1, 2, 3];
const iterator = array[Symbol.iterator]();
console.log('next' in iterator); // true
console.log(iterator.next()); // { value: 1, done: false}
console.log(iterator.next()); // { value: 2, done: false}
console.log(iterator.next()); // { value: 3, done: false}
console.log(iterator.next()); // { value: undefined, done: true}
빌트인 이터러블 | Symbol.iterator 메서드 |
---|---|
Array | Array.prototype[Symbol.iterator] |
String | String.prototype[Symbol.iterator] |
Map | Map.prototype[Symbol.iterator] |
Set | Set.prototype[Symbol.iterator] |
TypedArray | TypedArray.prototype[Symbol.iterator] |
arguments | arguments[Symbol.iterator] |
DOM 컬렉션 | NodeList.prototype[Symbol.iterator] HTMLCollection.prototype[Symbol.iterator] |
for ... of
문
이터러블을 순회하면서 이터러블의 요소를 변수에 할당
for (변수선언문 of 이터러블) { ... }
for ... of
문은 for ... in
문과 유사하지만,[[Enumerable]]
값이 true인 프로퍼티를 순회하며 열거next
메서드를 호출하여 이터러블 순회for (const item of [1, 2, 3]) {
console.log(item); // 1, 2, 3
}
유사 배열 객체
마치 배열처럼 인덱스로 프로퍼티 값에 접근할 수 있고 length 프로퍼티를 갖는 객체
for문으로 순회 가능하고, 인덱스로 프로퍼티 값 접근 가능
유사 배열 객체는 이터러블이 아닌 일반 객체
→ Symbol.iterator
메서드가 없으므로 for...of
문으로 순회할 수 없음❗️
const arrayLike = {
0: 1,
1: 2,
2: 3,
length: 3
};
for (let i = 0; i < arrayLike.length; i++) {
console.log(arrayLike[i]); // 1 2 3
}
// for ... of 문으로 순회 불가능
for (const item of arrayLike) {
console.log(item); // 1 2 3
}
// TypeError: arrayLike is not iterable
arguments
, NodeList
, HTMLCollection
은 유사 배열 객체이면서 이터러블Array.from
메서드를 사용해서 배열로 변환하여 반환const arrayLike = {
0: 1,
1: 2,
2: 3,
length: 3
};
const arr = Array.from(arrayLike);
console.log(arr); // [1, 2, 3]
이터레이션 프로토콜은
다양한 데이터 공급자가 하나의 순회 방식을 갖도록 규정하여 데이터 소비자가 효율적으로 다양한 데이터 공급자를 사용할 수 있도록,
데이터 소비자와 데이터 공급자를 연결하는 인터페이스 역할
const fibonacci = {
[Symbol.iterator]() {
let [pre, cur] = [0, 1];
const max = 10;
return {
next() {
[pre, cur] = [cur, pre + cur];
return { value: cur, done: cur >= max };
}
};
}
};
for (const num of fibonacci) {
console.log(num); // 1 2 3 5 8
}
const arr = [... fibonacci];
console.log(arr);
const [first, second, ...rest] = fibonnaci;
console.log(first, second, rest); // 1 2 [3, 5, 8]
const fibonacciFunc = function (max) {
let [pre, cur] = [0, 1];
return {
[Symbol.iterator] () {
return {
next() {
[pre, cur] = [cur, pre + cur];
return { value: cur, done: cur >= max };
}
};
}
};
};
for (const num of fibonacciFunc(10)) {
console.log(num); // 1 2 3 5 8
}
const iterable = fibonacciFunc(5);
const iterator = iterable[Symbol.iterator]();
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: 5, done: false }
const fibonacciFunc = function (max) {
let [pre, cur] = [0, 1];
return {
[Symbol.iterator] () { return this; },
next () {
[pre, cur] = [cur, pre + cur];
return { value: cur };
}
};
};
for (const num of fibonacciFunc()) {
if (num > 10000) break;
console.log(num); // 1 2 3 5 8 ... 4181 6765
}
const [f1, f2, f3] = fibonacciFunc();
console.log(f1, f2, f3); // 1 2 3