반복 가능한(iterable, 이터러블) 객체는 배열을 일반화한 객체이다. 이터러블 이라는 개념을 사용하면 어떤 객체에든 for..of 반복문을 적용할 수 있다.
반복 가능한 객체를 다른 객체와 구분짓는 특징은, 객체의 Symbol.iterator 속성에 특정 형태의 함수가 들어있다면, 이를 반복 가능한 객체 혹은 줄여서 iterable이라 부르고, 해당 객체는 iterable protocol을 만족한다고 말한다.
내장된 생성자 중 iterable 객체를 만들어내는 생성자에는 아래와 같은 것들이 있다.
StringArrayTyped arrayMapSetIterator 객체에는 for..of 구문을 사용할 수 있다.
for (variable of iterable) {
//구문
}
variable에는 const, let, var로 정의한 어떤 변수든 가능하고 iterable에는 iterable 객체만 가능하다.
let arr = ‘iterable’;
for ( let value of arr ){
console.log(value);
}
이터러블로 만들기 위해서 객체에 Symbol.iterator라는 메서드를 추가해 아래와 같은 일이 벌어지도록 해야한다.
1. for..of가 시작되자마자 for..of는 Symbol.iterator를 호출한다(Symbol.iterator가 없으면 에러가 발생다). Symbol.iterator는 반드시 이터레이터(iterator, 메서드 next가 있는 객체) 를 반환해야 한다.
2. 이후 for..of는 반환된 객체(이터레이터)만을 대상으로 동작한다.
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
}
배열과 문자열은 가장 광범위하게 쓰이는 내장 이터러블이다.
for..of는 문자열의 각 글자를 순회한다.
for (let char of "test") {
// 글자 하나당 한 번 실행됩니다(4회 호출).
alert( char ); // t, e, s, t가 차례대로 출력됨
}
서로게이트 쌍(surrogate pair)에도 잘 동작한다.
let str = '𝒳😂';
for (let char of str) {
alert( char ); // 𝒳와 😂가 차례대로 출력됨
}
let str = "Hello";
// for..of를 사용한 것과 동일한 작업을 합니다.
// for (let char of str) alert(char);
let iterator = str[Symbol.iterator]();
while (true) {
let result = iterator.next();
if (result.done) break;
alert(result.value); // 글자가 하나씩 출력됩니다.
}
이터레이터를 명시적으로 호출하는 경우는 거의 없는데, 이 방법을 사용하면 for…of를 사용하는 것보다 반복 과정을 더 잘 통제할 수 있다는 장점이 있다. 반복을 시작헀다가 잠시 멈춰 다른 작업을 하다가 다시 반복을 시작하는 것과 같이 반복 과정을 여러 개로 쪼개는 것이 가능하다.
Symbol.iterator가 구현된 객체이다.length 프로퍼티가 있어서 배열처럼 보이는 객체이다.이터러블 객체이면서 유사배열 객체인 문자열이 대표적인 그 예이다.
이터러블 객체라고 해서 유사 배열 객체는 아니고, 유사 배열 객체라고 해서 이터러블 객체인 것도 아니다.
let arrayLike = { // 인덱스와 length프로퍼티가 있음 => 유사 배열
0: "Hello",
1: "World",
length: 2
};
// Symbol.iterator가 없으므로 에러 발생
for (let item of arrayLike) {}
Array.from는 이터러블이나 유사 배열을 받아 ‘진짜’ Array를 만들어준다.
유사 배열 객체나 반복 가능한 객체를 얕게 복사해 새로운 Array 객체를 만든다.
이 과정을 거치면 이터러블이나 유사 배열에 배열 메서드를 사용할 수 있다.
let arrayLike = {
0: "Hello",
1: "World",
length: 2
};
let arr = Array.from(arrayLike); // (*)
alert(arr.pop()); // World (메서드가 제대로 동작합니다.)
Array.from은 객체를 받아 이터러블이나 유사 배열인지를 조사하고,
넘겨 받은 인수가 이터러블이나 유사 배열인 경우
새로운 배열을 만들고 객체의 모든 요소를 새롭게 만든 배열로 복사한다.