반복 가능한 객체(iterable object)는
for...of
구문과 함께 ES2015에 도입되었습니다, 반복 가능한 객체를 다른 객체와 구분짓는 특징은, 객체의Symbol.iterator
속성에 특별한 형태의 함수가 들어있다는 것입니다.
객체는Symbol.iterator
속성에 특정한 형태의 함수가 들어있다면, 이를 반복 가능한 객체(iterable object) 혹은 줄여서 iterable이라 부르고, iterable protocol을 만족한다고 말합니다.
어떤 객체가 Iterable이라면, 그 객체에 대해서는 아래의 기능들을 사용할 수 있습니다.
for...of
루프...
)즉, 문자열에 대해서도 위 기능들을 사용할 수 있습니다.
// for of
for(let c of 'hello') {
console.log(c);
}
// spread 연산자
const characters = [...'hello'];
// 분해대입
const [c1, c2] = 'hello';
// Array from은 iterable 혹은 array-like 객체를 인수로 받습니다.
Array.from('hello');
iterable
객체는iterable protocol
을 만족한다. 즉,Symbol.iterator
속성에 특별한 형태의 함수가 저장되어 있다. Iterable protocol을 만족하려면,symbol.iterator
속성에 저장되어 있는 함수 iterator객체를 반환해야 합니다.
Iterator 객체는 아래의 특별한 조건을 만족하는 객체입니다.
next
라는 메소드를 갖습니다.next
메소드는 다음 두 속성을 갖는객체를 반환해야 합니다.value
- 현재 순서의 값을 나타냅니다.done
- 반복이 모두 끝났는지 나타냅니다.위 조건을 iterator protocol 이라고 합니다.
// 문자열은 iterable이므로 이로부터 iterator를 생성할 수 있습니다.
const strIterator = 'go'[Symbol.iterator]();
strIterator.next(); // { value: 'g', done: false }
strIterator.next(); // { value: 'o', done: false }
strIterator.next(); // { value: undefined, done: true }
strIterator.next(); // { value: undefined, done: true }
우리는 직접 iterable인 객체를 만들 수 있을까요? 답은 Yes입니다
iterable protocol
을 구현하기만 하면 어떤 객체든iterable
이 될 수 있습니다.
Iterable 구현하기 가장 쉬운 방법은 ES2015에 도입된 generator 함수를 사용하는 것입니다.
// generator 함수 선언하기
function* gen1(){
// ...
}
// 표현식 사용하기
const gen2 = function*(){
// ...
}
// 메소드 문법으로 사용하기
const obj = {
* gen(){
// ...
}
}
Generator 함수를 호출하면 객체가 생성되는데, 이객체는 iterable protocol을 만족합니다. 즉 Symbol.iterator
속성을 가지고 있습니다.
for..of
가 동작하기위해서는 객체에Symbol.iterator(특수 내장 심볼)
메서드를 추가하여 아래의 순서가 진행되어야 합니다.
1.for..of
가 시작되자마자for..of
는Symbol.iterator
를 호출Symbol.iterator
가 없다면 에러가 발생,Symbol.iterator
는 반드시 iterator(메서드 next가 있는 객체) 를 반환해야 함
2. 이후for..of
는 반환된 객체(iterator)만을 대상으로 동작
3.for..of
에 다음 값이 필요하면,for..of
는 이터레이터next()
메서드를 호출
4.next()
의 반환값은{done: Boolean, value: any}
와 같은 형태이어야 함,done=true
는 반복이 종료 되었음을 의미done=false
일땐value
에 다음값이 저장
let range = {
from: 1,
to: 5
};
// 1. for..of 최초 호출 시, Symbol.iterator가 호출됩니다.
range[Symbol.iterator] = function() {
// Symbol.iterator는 이터레이터 객체를 반환합니다.
// 2. 이후 for..of는 반환된 이터레이터 객체만을 대상으로 동작하는데, 이때 다음 값도 정해집니다.
return {
current: this.from,
last: this.to,
// 3. for..of 반복문에 의해 반복마다 next()가 호출됩니다.
next() {
// 4. next()는 값을 객체 {done:.., value :...}형태로 반환해야 합니다.
if (this.current <= this.last) {
return { done: false, value: this.current++ };
} else {
return { done: true };
}
}
};
};
// 이제 의도한 대로 동작합니다!
for (let num of range) {
alert(num); // 1, then 2, 3, 4, 5
}
관심사의 분리(Separation of concern,SoC)
range
엔 메서드 next()
가 없음range[Symbol.iterator]()
를 호출해서 만든 iterator 객체와 이 객체의 메서드 next()
에서 반복에 사용될 값을 만들어냄객체를 iterator로
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
}
range[Symbol.iterator]()
가 객체 range
를 반환, 반환된 객체엔 필수 메서드 next()
가 있고 this.current
에 반복이 얼마나 진행 되었는지 나타내는 값도 저장되어 있음이터러블과 유사 배열은 비슷해 보지만 정의와 특성이 다릅니다.
- 이터러블(iterable) -
Symbol.iterator
가 구현된 객체 (for..of
사용 가능)- 유사 배열(array-like) -
index
와length
프로퍼티가 있어서 배열처럼 보이는 객체
이터러블과 유사 배열은 배열이 아니기 때문에
push
, pop
등의 메서드는 지원하지 않습니다.
Array.from 을 사용하여 이터러블이나 유사 배열을 진짜 Array
로 만들 수 있습니다. 이 과정을 거치면 이터러블이나 유사 배열에도 배열 메서드를 사용 할 수 있습니다.
let arrayLike = {
0: "Hello",
1: "World",
length: 2
};
let arr = Array.from(arrayLike); //(*)
console.log(arr.pop()); // World
자료 출처
https://ko.javascript.info/iterable - 모던 자바스크립트 튜토리얼
https://helloworldjavascript.net/pages/260-iteration.html - hello javascript