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
이라는 개념이 도입되었다.