반응형
본 내용은 이웅모 님의 "모던 자바스크립트 DeepDive" 책을 보고 정리한 내용입니다. 저작권 보호를 위해 책의 내용은 요약되었습니다.
이터레이션 프로토콜
이터레이션 프로토콜은 자바스크립트의 순회가능한 데이터 컬렉션(배열, 맵, 셋 등)을 정의하고 규격화하기 위해 약속한 규칙이다. ES6부터는 데이터 컬렉션을 이터러블로 통일하였고 이터러블 프로토콜과 이터레이션 프로토콜이 있다.
이터러블 프로토콜
- Symbol.iterator를 프로퍼티 키로 사용한 메서드를 직접 구현하거나 상속 받은 Symbol.iterator 메서드를 호출하여 이터레이터를 반환한다. 이러한 규약을 이터러블 프로토콜이라 하고 이 규약을 준수한 객체를 이터러블이라 한다.
- 이터러블은 for ... of문, 스프레드 문법 배열 디스트럭처링 할당의 대상으로 사용할 수 있다.
이터레이터 프로토콜
- Symbol.iterator 메서드를 호출하면 이터레이터 프로토콜을 준수한 이터레이터를 반환한다.
- 이터레이터는 next 메서드를 가지고 있어서 컬렉션의 각 요소를 하나씩 반환하고, 순회 과정을 관리한다.
- { value, done } 형태의 객체를 반환한다. value는 현재 요소의 값이고, done은 순회가 완료되었을 때 true가 되며, 더 이상 순회할 요소가 없을 때 false가 된다.
- 이터러블의 요소를 탐색하기 위한 포인터 역할을 한다.
이터러블이란?
- Symbol.iterator를 프로퍼티 키로 사용한 메서드를 직접 구현하거나 프로토타입 체인을 통해 상속받은 객체이다.
- Array, String, Map, Set 등은 이터러블이다.
- 이터러블 객체는 for...of문을 이용한 순회나 스프레드 문법과 배열 디스트럭처링 할다의 대상으로 사용할 수 있다.
- 일반 객체는 리터러블 프로토콜을 준수하지 않아 스프레드 문법 사용이 불가능했으나, TC39 프로세스의 stage 4단계에 제안되어 있는 스프레드 프로퍼티 제안에 의해 스프레드 문법 사용을 허용한다.
예시) 이터러블 확인
const isIterable = v => v!== null && typeof v[Symbol.iterator] === 'function';
// Array, String, Map, Set 등은 이터러블
console.log(isIterable([])); // 출력 : true
console.log(isIterable('')); // 출력 : true
console.log(isIterable(new Map())); // 출력 : true
console.log(isIterable(new Set())); // 출력 : true
console.log(isIterable({})); // 출력 : false
예시) for...of, 스프레드 문법, 배열 디스터럭처링 할당
const array = [1, 2, 3, 4, 5];
// 배열은 Array.prototype의 Symbol.iterator 메서드를 상속받는 이터러블이다.
console.log(Symbol.iterator in array); // 출력: true
// 1. for...of
for (const a of array) {
console.log(a); // 출력:1 2 3 4 5
}
// 2. 스프레드
console.log([ ... arr]); // 출력:[ 1, 2, 3, 4, 5 ]
// 3. 디스트럭처링 할당
const [a, ... rest] = arr;
console.log(a, rest); // 출력:1 [ 2, 3, 4, 5 ]
예시) 이터러블이 아닌 객체의 객체의 스프레드 문법 사용
const propertie = {a : 1}
console.log({...propertie}) // 출력:{a:1}
이터레이터 (Iterator) 란?
- 이터레이터는 반복 가능한 객체의 요소를 순회하고 접근하는 데 사용되는 객체이다.
- 이터러블의 Symbol.iterator 메서드를 호출하면 이터레이터 프로토콜을 준수한 이터레이터를 반환한다.
- 이터레이터는 next() 메서드와 [Symbol.iterator]()를 가진다.
next()
이 메서드는 다음 요소를 반환하는데 { done: false, value: 요소값 }와 같은 형태인 이터레이터 리절트(result) 객체를 반환한다. 다음 요소가 없을 경우에는 { done: true }를 반환한다.
[Symbol.iterator]()
이 메서드는 이터레이터 객체 자체를 반환하는데 이를 통해 객체를 순회할 수 있는 이터러블 객체로 만들 수 있다.
예시) 이터레이터 예시
const array = [1, 2, 3];
// Symbol.iterator 메서드는 이터레이터를 반환
const iterator = array[Symbol.iterator]();
// 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 }
빌트인 이터러블
자바스크립트에서는 이터레이션 프로토콜을 준수하는 빌터인 이터러블을 제공한다.
빌트인 이터럽블 | 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[Symbol.iterator], HTMLCollection[ Symbol.iterator ] |
for ... of 문
이터레이터의 next 메서드를 호출하여 반환된 이터레이터 객체의 value 프로퍼티값을 for .., of 문의 변수를 할당한다. 이터레이터 리절트 객체의 done 프로퍼티가 true 일때가지 이터러블을 순회한다.
예시)
const array = [1, 2, 3];
// Symbol.iterator 메서드는 이터레이터를 반환
const iterator = array[Symbol.iterator]();
for (item of iterator){
console.log(`item : ${item}`);
}
이터러블과 유사 배열 객체
- 유사 배열 객체란 배열처럼 인덱스를 프로퍼티로 가지는 객체이다. 이렇게 임의로 만든 유사 배열 객체는 Symbol.iterator 메서드가 없기에 for ... of 문으로 순회가 불가하다.
- Array.from 메서드를 사용하여 유사 배열 객체를 배열로 변환하면 for ... of문으로 순회할 수 있다.
예시) 유사배열 객체는 for ... of 순회가 불가
// 유사 배열 객체
const arrayLike = {
0: 1,
1: 2,
2: 3,
length: 3
};
// 유사 배열 객체는 for..of 순회가 불가하다
for (const item of arrayLike) { // TypeError: arrayLike is not iterable
console.log(item);
}
이터레이션 프로토콜의 필요성
- ES6 이전의 순회 가능한 데이터 컬렉션은 통일된 규약없이 다양한 방법으로 순회할 수 있었다.
- ES6 부터는 순회 가능한 데이터 컬렉션을 이터레이션 프로토콜을 준수하는 이터러블로 통일했다.
- 다양한 데이터 공급자(Array, Strting, Map/Set ...) 가 하나의 순회 방식을 갖도록 규정하여 효율성을 높힌다.
- 데이터 소비자와 데이터 공급자를 연결하는 인터페이스 역할을 한다.
사용자 정의 이터러블
- 이터러블이 아닌 일반 객체도 이터레이션 프로토콜을 준수하도록 구현하면 사용자정의 이터러블이 된다.
- 이터러블이면서 이터레이터인 객체를 생성하면 Symbol.iterator 메서드를 호출하지 않아도 된다.
예시) 피보나치 수열 구현
// 피보나치 수열 구현
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 sp = [...fibonacci]
console.log(sp); // 출력 : [ 1, 2, 3, 5, 8 ]
// 배열 디스럭처링 할당
const [first, ...rest] = fibonacci
console.log(first, rest); // 출력 : 1 [ 2, 3, 5, 8 ]
예시) 이터러블이면서 이터레이터인 객체 생성
// 이터러블이면서 이터레이터인 객체를 반환하는 함수이다.
const fibonacciFunc = (max) => {
let [pre, cur] = [0, 1];
// 이터레이터를 반환하는 Symbol.iterator 메서드를 구현하여
// 이터러블이면서 이터레이터인 객체를 생성한다.
return {
[Symbol.iterator]() {
return this;
},
next() {
[pre, cur] = [cur, pre + cur];
return { value: cur, done: cur >= max }
}
}
}
let iterator = fibonacciFunc(10);
for (const item of iterator) {
console.log(item); // 출력 : 1 2 3 5 8
}
iterator = fibonacciFunc(10);
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
/* 출력
{ value: 1, done: false }
{ value: 2, done: false }
{ value: 3, done: false }
{ value: 5, done: false }
{ value: 8, done: false }
{ value: 13, done: true }
*/
무한 이터러블과 지연 평가
- 배열, 문자열, Map, Set 등의 빌트인 이터러블은 데이터를 모두 메모리에 확보한 다음 동작하지만 이터러블은 Lazy evaluation(지연 평가)를 통해 값을 생성한다.
- for ... of 문은 이터러블을 순회할 때 next 메서드를 호출하는데 이 때 데이터가 생성된다.
- 지연 평가를 사용하면 필요한 데이터를 필요한 순간에 생성하므로 불필요한 메모리를 소비하지 않고 빠른 속도를 기대할 수 있다.
예시) 무한 이터러블
// 무한 이터러블을 생성하는 함수
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 > 100) break;
console.log(num); // 출력 : 1 2 3 5 8... 89
}
// 무한 이터러블에서 2개만을 취득한다.
const [first, second] = fibonacciFunc();
console.log(first, second); // 출력 : 1 2
반응형
'JavaScript' 카테고리의 다른 글
[자바스크립트] 디스트럭처링 할당이란? (1) | 2023.10.03 |
---|---|
[자바스크립트] 스프레드 문법 (0) | 2023.10.03 |
[자바스크립트] Symbol (0) | 2023.09.24 |
[자바스크립트] Number 생성자 (0) | 2023.09.16 |
[자바스크립트] 배열 고차 함수 (0) | 2023.08.30 |