이터러블
스프레드 문법
- 하나로 뭉쳐 있는 여러 값들의 집합을 전개하여 개별적인 값들의 목록으로 만드는 것
// ... [1, 2, 3]은 [1, 2, 3]을 개별 요소로 분리한다.(-> 1, 2, 3)
console.log(...[1, 2, 3]); // 1 2 3
consolg.log(...'Hello'); // H e l l o
배열 디스트럭처링 할당(구조 분해 할당)
- 구조화된 배열과 같은 이터러블 또는 객체를
destructuring
하여 1개 이상의 변수에 개별적으로 할당하는것
const arr = [1, 2, 3];
//1, 2, 3을 각각 one, two, three에 개별적으로 할당
const [one, two, three] = arr;
console.log(one, two, three); // 1 2 3
이터레이션 프로토콜
- 순회 가능한 데이터 컬렉션을 만들기 위해 정의한 규칙
- 이터러블 프로토콜
Symbol.iterator
메서드를 호출하면 이터레이터 프로토콜을 준수한 이터레이터를 반환하는 것
- 즉, 이터레이터를 반환하는
**Symbol.iterator
메서드** 구현되어 있어야 함
- 이터레이터 프로토콜
- 이터러블의
Symbol.iterator
메서드를 호출 시 이터레이터 반환
- 이터레이터의
next
메서드를 호출하면 이터레이터 리절트 객체를 반환
- 즉, 이터레이터 리절트 객체를 반환하는
**next
메서드** 구현되어 있어야 함
이터러블
- 이터러블 프로토콜을 준수한 객체
- 반복 가능한 객체
Symbol.iterator
를 프로퍼티 키로 사용한 메서드를 직접 구현하거나 프로토타입 체인을 통해 상속받은 객체
//배열, 문자열, Map, Set 은 이터러블
isIterable([]); // true
isIterable(''); // true
isIterable(new Map()); // true
isIterable(new Set()); // true
//일반 객체는 이터러블이 아님
isIterable({}); // false
for ... of
문으로 순회할 수 있으며 스프레드 문법, 배열 디스트럭처링 할당의 대상으로 사용 가능
//**배열**
const array = [1, 2, 3];
// 배열은 Array.prototype의 Symbol.iterator 메서드를 상속받는 이터러블
console.log(Symbol.iterator in array); // true
for(const item of array) {
console.log(item); // 1 2 3
}
console.log([...array]); // [1, 2, 3]
const [a, ...rest] = array;
console.log(a, rest); // 1, [2, 3]
//문자열
const string = 'String';
console.log(Symbol.iterator in string); // TypeError(???)
for (const item of string) {
console.log(item); // S t r i n g
}
console.log([...string]); // [ 'S', 't', 'r', 'i', 'n', 'g' ]
const [a, ...rest] = string;
console.log(a, rest); // S [ 't', 'r', 'i', 'n', 'g' ]
//**Map**
const map = new Map([["a", 1], ["b", 2], ["c", 3]]);
console.log(Symbol.iterator in map); // true
for (const [key, value] of map) {
console.log(`${key} ${value}`); // a 1 b 2 c 3
}
console.log([...map]); // [ [ 'a', 1 ], [ 'b', 2 ], [ 'c', 3 ] ]
const [a, ...rest] = map;
console.log(a, rest); // [ 'a', 1 ] [ [ 'b', 2 ], [ 'c', 3 ] ]
//**Set**
const set = new Set([1, 2, 3]);
console.log(Symbol.iterator in set); // true
for (const value of set) {
console.log(value); // 1 2 3
}
console.log([...set]); // [1, 2, 3]
const [a, ...rest] = set;
console.log(a, rest); // 1 [2, 3]
//**일반 객체**
const obj = { a: 1, b: 2 };
//Symbol.iterator 메서드를 구현하거나 상속받지 않는다.
//이터러블 프로토콜을 준수한 이터러블이 아니다.
console.log(Symbol.iterator in obj); // false
//for ... of 사용 불가
for(const item of obj){ // TypeError
console.log(item);
}
//배열 디스트럭처링 할당 사용 불가
const [a, b] = obj; // TypeError
//**스프레드 문법 사용 가능**
console.log({ ...obj}); // { a: 1, b: 2 }
이터레이터
- 값을 차례대로 꺼낼 수 있는 객체
- 이터러블 요소를 탐색하기 위한 포인터의 역할
- 이터러블의
Symbol.iterator
메서드를 호출하면 이터레이터 프로토콜을 준수하며 **next
메서드를 갖는** 이터레이터를 반환
next
메서드를 호출하면 이터러블을 순차적으로 한 단계씩 순회하며 순회 결과를 나타내는 이터레이터 리절트 객체를 반환
이터레이터 리절트 객체
const array = [1, 2, 3];
//Symbol.iterator 메서드 호출 방법
//이터레이터를 반환한다.
const iterator = array[Symbol.iterator]();
//Symbol.iterator 메서드가 반환한 이터레이터는 next 메서드를 갖는다.
console.log('next' in iterator); // true
//next 메서드를 호출하면 이터레이터 리절트 객체를 반환
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 }
이터레이터 vs 배열
//배열
const array = [1, 2, 3, 4, 5, 6, 7];
//array[0] == 1, array[3] == 4
//array.length == 7
//이터레이터
const iterator = (function() {
let num = 1;
return {
next: function() {
return (
num > 7 ?
{ done: true } :
{ done: false, value: num++ }
);
}
};
})();
//오직 next() 메소드만이 존재
//done 과 value 항목을 담은 리절트 객체 반환
console.log(iterator.next().value); // 1
console.log(iterator.next().value); // 2
console.log(iterator.next().value); // 3
- 배열
- 이터레이터
- 이전에 얻어온 값 바로 다음 값만 가져올 수 있음
- 배열의
map()
메소드와 비슷한 기능
기능이 한 가지밖에 없는 이터레이터가 존재하는 이유는?
→ 높은 가독성, 메모리 효율적 사용
빌트인 이터러블
빌트인 이터러블 |
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 이터러블) { ... }
- 이터레이터의
next
메서드 호출하여 이터러블 순회
- 이터레이터 리절트 객체의
value
프로퍼티 값을 for ... of
문의 변수에 할당
done
프로퍼티 값이 false
이면 1번으로
done
프로퍼티 값이 true
이면 순회 중단
const iterable = [1, 2, 3];
//for 문
//이터러블의 Symbol.iterator 메서드를 호출하여 이터레이터를 생성
const iterator = iterable[Symbol.iterator]();
for(;;){
//next 메서드는 이터레이터 리절트 객체 반환
const res = iterator.next();
if(res.done) break;
const item = res.value;
console.log(item); // 1 2 3
}
//for...of 문
for(const item of [1, 2, 3]) {
console.log(item); // 1 2 3
}
next
메서드 호출 시
{ value: 1, done: false }
res.done
=== false
이므로 계속 순회, item
은 1
{ value: 2, done: false }
res.done
=== false
이므로 계속 순회, item
은 2
{ value: 3, done: false }
res.done
=== false
이므로 계속 순회, item
은 3
{ value: undefined, done: true }
res.done
=== true
이므로 순회 종료
이터러블과 유사 배열 객체
//유사 배열 객체
const arrayLike = {
0: 1,
1: 2,
2: 3,
length: 3
};
//for...of 문 사용 불가
for(const item of arrayLike) {
console.log(item); //TypeError: arrayLike is not iterable
}
//배열 디스트럭처링 할당 사용 불가
const [a, b, c] = arrayLike; //TypeError: arrayLike is not iterable
//**스프레드 문법 사용 가능**
console.log({ ...arrayLike }); // { '0': 1, '1': 2, '2': 3 }
//**Array.from**은 유사 배열 객체 또는 이터러블을 배열로 변환
const arr = Array.from(arrayLike);
이터레이션 프로토콜의 필요성
- ES6 이전의 순회 가능한 데이터 컬렉션은 통일된 규약 없이 나름의 구조를 가지고 순회할 수 있었음
- ES6에서는 이들을 이터레이션 프로토콜을 준수하는 이터러블로 통일하여 일원화하였다.

- 이터레이션 프로토콜은 데이터 소비자와 데이터 공급자를 연결하는 인터페이스 역할을 한다.
사용자 정의 이터러블
- 이터러블이 아닌 일반 객체를 이터레이션 프로토콜을 준수하도록 구현한 것
사용자 정의 이터러블 구현
const fibonacci = {
//Symbol.iterator 메서드를 구현
[Symbol.iterator](){
let [pre, cur] = [0, 1];
const max = 10;
//Symbol.iterator 메서드는 next 메서드를 소유한 이터레이터 반환
return {
next(){
[pre, cur] = [cur, pre + cur];
//next 메서드는 이터레이터 리절트 객체 반환
return { value: cur, done: cur >= max };
}
};
}
};
for (const num of fibonacci) {
console.log(num); // 1 2 3 5 8
}
Symbol.iterator
메서드를 구현
Symbol.iterator
메서드가 next
메서드를 갖는 이터레이터 반환
next
메서드는 이터레이터 리절트 객체 반환
이터러블을 생성하는 함수
- 고정적이었던 값을 인수로 전달받아 이터러블을 반환하는 함수를 생성
//수열의 최대값을 인수로 전달받음
const fibonacciFunc = function (max) {
let [pre, cur] = [0, 1];
//Symbol.iterator 메서드를 구현한 이터러블 반환
return {
[Symbol.iterator]() {
return {
next(){
[pre, cur] = [cur, pre + cur];
//next 메서드는 이터레이터 리절트 객체 반환
return { value: cur, done: cur >= max };
}
};
}
};
};
for (const num of fibonacci) {
console.log(num);
}
이터러블이면서 이터레이터인 객체를 생성하는 함수
//1. next 메서드 구현 => 이터레이터의 조건
{
next: () => {}
}
//2. [Symbol.iterator]라는 이름의 iterator 구현 => 이터러블의 조건
{
next: () => {},
[Symbol.iterator]: function() {}
}
//3. [Symbol.iterator]는 iterator 로서 구현되어야 하기 때문에 next 메서드 필요
{
next: () => {},
[Symbol.iterator]: function() {
return this;
}
}
//이터러블이면서 이터레이터인 객체
//이터레이터를 반환하는 Symbol.iterator 메서드와 이터레이션 리절트 객체를 반환하는 next 메서드 소유
{
[Symbol.iterator]() { return this; }
next() {
return { value: any, done: boolean }
}
}
// 이터러블이면서 이터레이터인 객체를 반환하는 함수
const fibonacciFunc = function (max) {
let [pre, cur] = [0, 1];
// Symbol.iterator 메소드와 next 메소드를 소유한
// 이터러블이면서 이터레이터인 객체를 반환
return {
// Symbol.iterator 메소드
[Symbol.iterator]() {
return this;
},
// next 메소드는 이터레이터 리절트 객체를 반환
next() {
[pre, cur] = [cur, pre + cur];
return {
value: cur,
done: cur >= max
};
}
};
};
// iter는 이터러블이면서 이터레이터이다.
let iter = fibonacciFunc(10);
// iter는 이터레이터이다.
console.log(iter.next()); // {value: 1, done: false}
console.log(iter.next()); // {value: 2, done: false}
console.log(iter.next()); // {value: 3, done: false}
console.log(iter.next()); // {value: 5, done: false}
console.log(iter.next()); // {value: 8, done: false}
console.log(iter.next()); // {value: 13, done: true}
iter = fibonacciFunc(10);
// iter는 이터러블이다.
for (const num of iter) {
console.log(num); // 1 2 3 5 8
}
무한 이터러블과 지연 평가
// 무한 이터러블을 생성하는 함수
const fibonacciFunc = function () {
let [pre, cur] = [0, 1];
return {
[Symbol.iterator]() {
return this;
},
next() {
[pre, cur] = [cur, pre + cur];
// done 프로퍼티를 생략한다.
return { value: cur };
}
};
};
// fibonacciFunc 함수는 무한 이터러블을 생성한다.
for (const num of fibonacciFunc()) {
if (num > 10000) break;
console.log(num); // 1 2 3 5 8...
}
// 배열 디스트럭처링 할당 가능
// 무한 이터러블에서 3개만을 취득한다.
const [f1, f2, f3] = fibonacciFunc();
console.log(f1, f2, f3); // 1 2 3
- 이터러블은 값을 생성하는데, 지연 평가를 통해 평가를 늦춘다.
- 위 함수는 무한 이터러블 생성 ⇒ 데이터 소비자 등장 전까지는 데이터를 생성하지 않음
- 데이터가 필요할 때까지 생성을 지연하다가 필요한 순간 생성