용어 정리이터러블, 이터레이터 이터러블(iterable)이터레이터(iterator)[Symbol.iterator]이터러블 구현하기이터러블이 순회하는 과정이터러블, 유사 배열 객체직접 구현한 이터러블, 유사 배열 객체의 문제점Array.from
용어 정리
컬렉션
: 컬렉션 자료형이란 여러 가지 요소를 하나로 묶어 사용하는 데이터 타입입니다.
프로토콜
: 프로토콜은 컴퓨터 내부에서, 또는 컴퓨터 사이에서 데이터의 교환 방식을 정의하는 규칙 체계입니다.
이터러블, 이터레이터
이터러블(iterable)
- 이터러블의 정의는 Symbol.iterator를 프로퍼티 키로 사용한 메서드를 직접 구현하거나 프로토타입 체인을 통해 상속받은 Symbol.iterator 메서드를 호출하면 이터레이터 객체를 반환하는 객체입니다.
- 이터러블 정의를 따르는 규약을 이터러블 프로토콜이라고 지칭합니다.
- 이터러블은
for … of
문으로 순회할 수 있으며, 스프레드 문법과 배열 디스트럭처링 할당의 대상으로 사용할 수 있습니다.
- 대표적인 빌트인 이터러블은
Array
,TypeArray
,String
,Set
,Map
,DOM 컬렉션
,arguments
가 있습니다.
console.log(Symbol.iterator in Array.prototype); // true console.log(Symbol.iterator in String.prototype); // true console.log(Symbol.iterator in Map.prototype); // true console.log(Symbol.iterator in Set.prototype); // true console.log(Symbol.iterator in Object.prototype); // false
이터레이터(iterator)
- 이터레이터의 정의는
{ done: boolean, value: 값}
형태의 객체를 반환하는 next 메서드를 가진 객체입니다. 또한 이터레이터는 Symbol.iterator의 반환값입니다.
- 이터레이터 정의를 따르는 규약을 이터레이터 프로토콜이라고 지칭합니다.
const iterator = [1, 2, 3][Symbol.iterator](); console.log('next' in iterator); // true console.log(iterator.next()); // { value: 1, done: false }
이터레이션 프로토콜은 순회 가능한 데이터 컬렉션 자료구조를 만들기 위해 ES6 사양에 정의된 약속된 규칙입니다.
[Symbol.iterator]
위의 설명에서 [Symbol.iterator]라는 용어가 자주 등장했습니다. [Symbol.iterator] 는 이터러블을 구현하기 위해 이터레이션 프로토콜에서 정의한 규칙입니다. [Symbol.iterator]를 사용해서 직접 이터러블을 구현해봅시다.
이터러블 구현하기
const range = { from: 1, to: 5, }; range[Symbol.iterator] = function () { return { current: this.from, last: this.to, next() { if (this.current <= this.last) { return { done: false, value: this.current++ }; } else { return { done: true }; } }, }; }; for (const number of range) { console.log(number); // 1, 2, 3, 4, 5 }
- range 객체를 이터러블로 구현하기 위해서는 [Symbol.iterator] 메서드를 추가해야 합니다.
- 이터러블을 만들기 위해서는 [Symbol.iterator] 메서드는 이터레이터를 반환해야 합니다. 즉 next 메서드를 가지고 있는 객체를 반환해야 합니다.
- 이터레이터를 만들기 위해서는
{ done: boolean, value: 값}
형태의 객체를 반환해야 합니다. (반복해서 구하고 싶은 값에 대한 특정한 조건문 추가)
- range 객체가 이터러블인지 확인하기 위해서
for ... of
를 통해 확인합니다.
직접 이터러블을 만들어보는 과정을 거치면서 확실하게 알게 된 점이 있습니다. 그것은 바로 이터러블을 구현하기 위해서는 이터레이터 프로토콜과 이터러블 프로토콜을 조합해야 합니다. 또한 명확히 보이는 부분은 ranage 객체는 이터러블이고 [Symbol.iterator]가 반환하는 객체가 이터레이터라는 점도 알 수 있습니다.
// range 자체를 이터레이터로 만들면 코드가 더 간단해집니다. let range = { from: 1, to: 5, [Symbol.iterator]() { this.current = this.from; return this; }, next() { if (this.current <= this.to) { return { done: false, value: this.current++ }; } else { return { done: true }; } } }; for (let num of range) { alert(num); // 1, then 2, 3, 4, 5 }
이터러블이 순회하는 과정
for...of
가 시작되자마자for...of
는 [Symbol.iterator]를 호출합니다.
- 이후
for...of
는 반환된 객체(이터레이터)만을 대상으로 동작합니다.
for...of
에 다음 값이 필요하면,for…of
는 이터레이터의 next 메서드를 호출합니다.
- next()의 반환 값은
{done: Boolean, value: any}
와 같은 형태이어야 한다. 그래야 순회가 된다.done = true
는 반복이 종료되었음을 의미한다.done = false
일땐value
에 다음 값이 저장됩니다.
이터러블, 유사 배열 객체
- 이터러블: 이터러블 프로토콜 조건을 충족하는 객체입니다.
- 유사 배열 객체: 인덱스와 length 프로퍼티를 가지고 있는 객체로 배열처럼 동작하지만 배열이 아닙니다.
const lkeArray = { 0: 10, 1: 20, 2: 30, length: 3, }; for (const value of likeArray) { console.log(value); } // TypeError: likeArray is not iterable
예시의
likeArray
객체는 유사 배열 객체 조건에는 충족하지만 이터러블 프로토콜 조건에는 충족하지 못합니다.직접 구현한 이터러블, 유사 배열 객체의 문제점
- 직접 구현한 이터러블과 유사 배열은 실제 배열이 아닙니다.
- 이런 문제로 Arrray.prototype을 통한 상속 메서드(
map
,filter
)를 사용할 수 없습니다.
- 그렇다면 어떻게 해결할 수 있을까요?
Array.from
Array.from
메서드는 유사 배열 객체나 이터러블을 얕게 복사해 새로운Array
객체를 만듭니다.const likeArray = { 0: 10, 1: 20, 2: 30, length: 3 }; console.log(Array.from(likeArray)); // [10, 20, 30] const arr = Array.from(likeArray); for (const value of arr) { console.log(value); // 10 20 30 }