Array는 for 문이나 while 문으로 순회할 수 있어 iterable하다iterable하지 않다const myFavoriteAuthors = {
allAuthors : {
fiction : [
'Agatha Christie',
'J. K. Rowling',
'Dr. Seuss'
],
scienceFiction : [
'Neal Stephenson',
'Arthur Clarke',
'Isaac Asimov',
'Robert Heinlein',
],
fantasy : [
'J. R. R. Tolkien',
'J. K. Rowling',
'Terry Pratchett',
]
},
}
for(let author of myFavoriteAuthors){
console.log(author);
}
// TypeError : {} is not iterable
const myFavoriteAuthors= {
allAuthors : {
...
},
getAllAuthors(){
const authors = [];
for(const author of this.allAuthors.fiction){
authors.push(author);
}
for(const author of this.allAuthors.scienceFiction) {
authors.push(author);
}
for(const author of this.allAuthors.fantasy){
authors.push(author);
}
return authors;
}
하지만 몇 가지 문제점이 있다.
getAllAuthors는 너무 구체적이다. 다른 사람들이 그들만의 myFavoriteAuthors을 retrieveAllAuthors라고 새로 만들었다고 생각해보자
개발자라면 모든 데이터를 반환하는 특수한 메서드들을 알 필요가 있고, 위의 예제에선 getAllAuthors가 그러한 메서드이다.
getAllAuthors는 모든 author의 문자열 배열을 반환한다.
만약 다른 개발자가 다른 형태의 배열을 반환한다면…?
[ { name : 'Agatha Christie' }, { name : 'J. K. Rowling' }, ... }
개발자는 모든 데이터를 반환하는 메서드의 정확한 이름과 반환 값을 알아야만 한다.
반환 값이 고정이고 불변하는 메서드의 이름을 정한 것이 iteratorMethod이다.
ECMA가 비슷한 단계를 밟아 만든 것의 이름이 Symbol.iterator이다.
Symbol은 다른 프로퍼티의 이름과 절대 충돌하지 않는 고유한 이름을 제공한다Symbol.iterator는 iterator라는 객체를 반환한다.iterator는 next라는 메서드를 갖고 next 메서드는 value와 done 이라는 키 값을 가진 객체를 반환한다value는 현재 값을 갖고 있는 key이고, 어떠한 형태도 가능하다.done은 boolean 값으로 모든 값을 가져왔는지 아닌지를 알려준다.
iterable은 public에게 각 요소들에 접근할 수 있게 하는 자료구조이다.iterable은 Symbol.iterator 메서드로 iterable의 요소에 접근할 수 있게 해주고 iterator를 만든다.iterator는 자료 구조의 요소들을 순회하는 포인터이다.const iterable = {
[Symbol.iterator]() {
let step = 0;
const iterator = {
next() {
step++;
if(step === 1){
return { value : 'This', done : false };
}else if(step === 2) {
return { value : 'is', done : false };
}else if(step === 3) {
return { value : 'iterable.', done : false };
}
return { value : undefined, done : true };
}
};
return iterator;
};
var iterator = iterable[Symbol.iterator]();
iterator.next() // { value : 'This', done : false }
iterator.next() // { value : 'is', done : false }
iterator.next() // { value : 'iterable', done : false }
iterator.next() // { value : undefined, done : true }
iterator는 next 메서드를 갖는 객체이고, iterable의 Symbol.iterator 메서드로 만들 수 있다.
위의 예제에서 iterator는 iterable 객체를 생성하는 Symbol.iterator 메서드 내의 step 변수에 따라 next 메서드가 반환하는 value가 달라지고, step의 값이 3보다 커지면 done 값이 true가 된다.
위의 예제와 같은 원리로 for-of 반복문이 실행된다. for-of 반복문은 iterable을 받고 그것의 iterator를 생성한 뒤, next()의 done이 true가 될 때까지 반복한다.
let str = 'Hello';
let iterator = str[Symbol.iterator](); // StringIterator
while(true){
const { value, done } = iterator.next();
if(done) break;
console.log(value);
}
// H e l l o, for(let ch of str) 과 같다.
let set = new Set();
set.add(10), set.add(20), set.add(5), set.add(1);
let iterator = set[Symbol.iterator](); // SetIterator
while(true){
const { value, done } = iterator.next();
if(done) break;
console.log(value);
}
// 10, 20, 5, 1 for(let value of set) 와 같다.
배열의 구조분해 할당 또한 iteable이기 때문에 가능하다
const array = [1, 2, 3, 4, 5];
const [ one, two, three ] = array;
// 위의 코드와 아래의 코드는 같다
const array = [1, 2, 3, 4, 5];
const iterator = array[Symbol.iterator]();
const one = iterator.next().value;
const two = iterator.next().value;
const trhee = iterator.next().value;
... 연산자
const array = [10, 11, 12];
const newArray = [1, ...array, 2, 3]
// 위와 아래의 코드는 같다
const array = [10, 11, 12];
const iterator = array[Symbol.iterator]();
const newArray = [1];
for(let nextValue = iterator.next(); nextValue.done !== true;
nextValue = iterator.next()){
next.push(nextValue.value);
}
nextArray.push(2);
nextArray.push(3);
출처 :
A Simple Guide to ES6 Iterators in JavaScript with Examples
iterable은 무엇인가?iterable은 반복 가능한, 순회가 가능한 자료구조를 뜻하고 구체적으로는 Symbol.iterator 메서드로 해당 데이터를 순회할 수 있는 iterator를 생성할 수 있는 iterable protocol을 만족하는 자료구조(객체)를 말한다.iterable은 왜 필요한가?for ... in 문으로 객체의 key 값을 순회하는 방법이 있었으나 그 순서를 보장할 수 없다
for ... in 문은 Enumerable한 속성들을 열거하는 기능을 가지고 있는데 브라우저의 구현에 따라 순서가 다르기 때문에 일관된 결과를 출력하지 못하기 때문이다.iterable이라는 개념이 도입되었다.