이터레이션 프로토콜
- 순회 가능한 데이터 컬렉션 (자료구조)을 만들기 위해 ECMAScript 사양에 정의하여 미리 약속한 규칙
이터러블 프로토콜
- Symbol.iterator 를 프로퍼티 키로 사용한 메서드를 직접 구현하거나 프로타입 체인을 통해 상속받은 메서드
- 이터러블 프로토콜을 준수한 객체
- 이터러블은 for ... of 문으로 순회할 수 있으며 스프레드 문법과 배열 디스트럭처링 할당의 대상으로 사용 가능
이터레이터 프로토콜
- Symbol.iterator 메서드를 호출하면 이터레이터 프로토콜을 준수한 이터레이터 반환
- 이터레이터 프로토콜을 준수한 객체

이터러블
- 이터러블 프로토콜을 준수한 객체로 Symbol.iterator 를 프로퍼티 키로 사용한 메서드를 직접 구현하거나 프로토 타입 체인을 통해 상속받은 객체
- Symbol.iterator 메서드를 직접 구현하지 않거나 상속받지 않은 일반 객체는 이터러블이 아님 (스프레드 문법 사용은 허용)
const isIterable = v => v !== null && typeof v[Symbol.iterator] === 'function';
// 배열, 문자열, Map, Set 등은 이터러블
isIterable([]); // true
isIterable(''); // true
isIterable(new Map()); // true
isIterable(new Set()); // true
isIterable({}); // false
이터레이터
- 이터러블의 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.prototype[Symbol.iterator] |
| Dom 컬렉션 | NodeList.prototype[Symbol.iterator] HTMLCollection.prototype[Symbol.iterator] |
for ... of 문
- 이터러블을 순회하면서 이터러블 요소를 변수에 할당
- for ... in 문은 객체 프로토타입 체인 상에 존재하는 모든 프로토타입의 프로퍼티 중 프로퍼티 어트리뷰트 [[Enumerable]] 의 값이 true인 프로퍼티를 순회하며 열거
- for ... of 문은 내부적으로 이터레이터의 next 메서드를 호출하여 이터러블 순회 (이터레이터 리절트 객체의 value 값을 변수에 할당, done 값이 false 면 계속 순회 true 면 중단)
for (변수선언문 of 이터러블) { ... }
for (변수선언문 in 객체) { ... }
const iterable = [1, 2, 3];
const iterator = iterable[Symbol.iterator]();
for (;;) {
const res = iterator.next();
if (res.done) break;
const item = res.value;
console.log(item); // 1 2 3
}
이터러블과 유사 배열 객체
- 배열처럼 인덱스로 프로퍼티 값에 접근 가능하며 length 프로퍼티를 갖는 객체
- for문으로 순회 가능하며 인덱스로 프로퍼티 값에 접근 가능
- 하지만 이터러블이 아닌 일반 객체로 Symbol.iterator 가 없기 때문에 for ... of 문으로 순회 불가능
const arrayLike = {
0: 1,
1: 2,
2: 3,
length: 3,
};
/*
for (const item of arrayLike) {
console.log(arrayLike[item]); // TypeError: arrayLike is not iterable
}
*/
const arr = Array.from(arrayLike);
console.log(arr); // [1, 2, 3]
이터레이션 프로토콜의 필요성
- 다양한 데이터 공급자가 각자의 순회 방식을 갖는다면 데이터 소비자는 다양한 데이터 공급자의 순회 방식을 모두 지원해야하며 이는 효율성이 떨어짐
- 이터러블을 지원하는 데이터 소비자는 내부에서 Symbol.iterator 메서드를 호출해 이터레이터를 생성하고 이터레이터의 next 메서드를 호출하여 이터러블을 순쇠하며 이터레이터 리절트 객체를 반환, 이터레이터 리절트 객체의 value/ done 프로퍼티 값을 취득
- 데이터 소비자와 데이터 공급자를 연결하는 인터페이스 역할

사용자 정의 이터러블
1. 사용자 정의 이터러블 구현
- 일반 객체도 이터레이션 프로토콜을 준수하도록 구현하면 사용자 정의 이터러블이 됨
// 파보나치 수열을 구현한 사용자 정의 이터러블
const fibonacci = {
// Symbol.iterator 메서드를 구현하여 이터러블 프로토콜 준수
[Symbol.iterator](){
let [pre, cur] = [0, 1] // 디스트럭처링 할당
const max = 10; // 수열의 최대값
return{
next(){
[pre, cur] = [cur, pre + cur]; // 디스트럭처링 할당
// 이터레이터 리절트 객체 반환
return { value : cur, done : cur >= max };
}
}
}
}
// 이터러블인 fibonacci 객체를 순회할 때마다 next 메서드가 호출
for(const num of fibonacci){
console.log(num); // 1 2 3 5 8
}
2. 이터러블을 생성하는 함수
// 수열의 최대값을 인수로 전달받음
const fibonacci function (max) {
let [pre, cur] = [0, 1];
// Symbol.iterator 메서드를 구현한 이터러블 반환
return{
[Symbol.iterator](){
return {
next(){
[pre, cur] = [pre, pre + cur];
return { value : cur, done : cur >= max };
}
}
}
}
}
// 이터러블을 반환하는 함수에 수열의 최대값을 인수로 전달하며 호출
// fibonacci(10)은 이터러블 반환
for(const num of fibonacci(10)){
console.log(num); // 1 2 3 5 8
}
3. 이터러블이면서 이터레이터인 객체를 생성하는 함수
const fibonacci function (max) {
let [pre, cur] = [0, 1];
// Symbol.iterator 메서드와 next 메서드를 소유한 이터러블이면서 이터레이터인 객체 반환
return{
[Symbol.iterator]() { return this; },
// next 메서드는 이터레이터 리절트 객체를 반환
next(){
[pre, cur] = [cur, pre + cur];
return { value : cur, done cur >= max };
}
};
};
// iter 는 이터러블 이면서 이터레이터
let iter = fibonacci(10)
for(const num of iter){
console.log(num); // 1 2 3 5 8
}
4. 무한 이터러블과 지연 평가
- 이터러블은 지연 평가를 통해 데이터 생성
- 지연 평가 : 데이터가 필요한 시점 이전까지는 미리 데이터를 생성하지 않다가 데이터가 필요한 시점이 되면 생성
- 이터레이터의 next 메서드를 호출하는 데 이 때 데이터 생성
- 빠른 실행 속도, 불필요한 메모리 소비 X, 무한 표현 가능
const fibonacci = function (max) {
let [pre, cur] = [0, 1];
return {
[Symbol.iterator]() {
return this;
},
next() {
[pre, cur] = [cur, pre + cur];
return { value: cur }; // done 생략 = 무한
},
};
};
for (const num of fibonacci()) {
if (num > 10000) break;
console.log(num); // 1 2 3 5 8 13 21 ... 6765
}
const [f1, f2, f3] = fibonacci();
console.log(f1, f2, f3); // 1 2 3'Javascript' 카테고리의 다른 글
| [모던 자바스크립트 Deep Dive] 33. 디스트럭처링 할당 (1) | 2023.11.28 |
|---|---|
| [모던 자바스크립트 Deep Dive] 32. 스프레드 문법 (0) | 2023.11.28 |
| [모던 자바스크립트 Deep Dive] 30. 7번째 데이터 타입 Symbol (1) | 2023.11.27 |
| [모던 자바스크립트 Deep Dive] 29. String (1) | 2023.11.25 |
| [모던 자바스크립트 Deep Dive] 28. RegExp (2) | 2023.11.25 |