본 포스트는 인프런의 함수형 프로그래밍과 JavaScript ES6+ 강의(링크)를 듣고 정리한 내용입니다.
const list = [1, 2, 3];
for (var i = 0; i < list.length; i++) {
console.log(list[i]);
}
list.length
라는 프로퍼티에 의존하고, 리스트 내부의 값을 숫자라는 key
로 순회 하도록 i
를 증가시키는 방식const list = [1, 2, 3];
for (const a of list) {
console.log(a);
}
Symbol.iterator
ES6에서 추가된 Symbol
Symbol.iterator는 어떤 객체의 키로 사용될 수 있다.
const arr = [1, 2, 3];
console.log(arr[Symbol.iterator]) //출력결과: ƒ values() { [native code] }
for (const a of arr) console.log(a);
//출력결과:
//1
//2
//3
array
는 key
값으로 배열의 내부 값에 접근 가능
만약 Symbol.iterator
의 값을 null
로 설정할 경우 순회가 되지 않는다.
arr[Symbol.iterator] = null;
for (const a of arr) console.log(a);
const set = new Set([1, 2, 3]);
console.log(set[Symbol.iterator]) //출력결과: ƒ values() { [native code] }
for (const a of set) console.log(a);
//출력결과:
//1
//2
//3
set
은 key
값으로 내부 값에 접근 불가능
for i++
의 방식으로는 배열 순회가 불가능 => for of
의 내부 구조는 for i++
와는 다르다.const map = new Map([['a', 1], ['b', 2], ['c', 3]]);
console.log(map[Symbol.iterator]) //출력결과: ƒ values() { [native code] }
for (const a of map) console.log(a);
//출력결과:
//(2) ['a', 1]
//(2) ['b', 2]
//(2) ['c', 3]
map
도 key
값으로 내부 값에 접근 불가능
이터러블: 이터레이터를 리턴하는 [Symbol.iterator]()
를 가진 값
Array
Set
Map
모두 iterable이다.이터레이터: { value, done }
객체를 리턴하는 next()
를 가진 값
let iterator = arr[Symbol.iterator]();
iterator.next() //{value: 1, done: false}
iterator.next() //{value: 2, done: false}
iterator.next() //{value: 3, done: false}
iterator.next() //{value: undefined, done: true}
done: true
가 된다.이터러블/이터레이터 프로토콜: 이터러블을 for...of
, 전개 연산자 등과 함께 동작하도록한 규약
const arr = [1, 2, 3];
let iter1 = arr[Symbol.iterator]();
iter1.next();
for (const a of iter1) console.log(a);
//2
//3
let iter2 = arr[Symbol.iterator]();
iter2.next();
iter2.next();
for (const a of iter2) console.log(a);
//3
next()
를 실행 하면 for...of
의 순회가 하나씩 뒤로 밀린다.for...of
는 이터러블한 객체의 이터레이터를 순회하며 실행된다.Set
Map
도 동일하다. (이터러블/이터레이터 프로토콜의 규약을 따른다.)const map = new Map([['a', 1], ['b', 2], ['c', 3]]);
map.keys(); //MapIterator {'a', 'b', 'c'}
var a = map.keys();
a.next(); //{value: 'a', done: false}
a.next(); //{value: 'b', done: false}
a.next(); //{value: 'c', done: false}
a.next(); //{value: undefined, done: true}
for (const a of map.keys()) console.log(a);
//a
//b
//c
for (const a of map.values()) console.log(a);
//1
//2
//3
for (const a of map.entries()) console.log(a);
//(2) ['a', 1]
//(2) ['b', 2]
//(2) ['c', 3]
map.keys()
라는 함수는 이터레이터를 리턴한다.
그리고 이 이터레이터를 실행하게 되면 value
의 key
만 남게 된다.
리턴된 이터레이터도 이터러블이다!!
for...of
로 key
값만 순회가 가능하다.value
를 뽑는 values()
와 내부의 key
와 value
를 entry
로 뽑아주는 entries()
라는 함수도 존재한다.
const iterable = {
[Symbol.iterator]() {
let i = 3;
return {
next() {
return i === 0 ? { done: true } : { value: i--, done: false };
},
};
},
};
let iterator = iterable[Symbol.iterator]();
// console.log(iterator.next()); //{ value: 3, done: false }
// console.log(iterator.next()); //{ value: 2, done: false }
// console.log(iterator.next()); //{ value: 1, done: false }
// console.log(iterator.next()); //{ done: true }
for (const a of iterable) {
console.log(a);
}
//3
//2
//1
for (const a of iterator) {
console.log(a);
}
//iterator is not iterable
iterable
을 직접 구현해 봤다.for...of
에 넣었을 때 iterator is not iterable
에러가 발생한다.well-formed
이터레이터가 자기자신을 반환하는
Symbol.iterator
를 가지고 있을때well-formed 이터레이터
라고 한다.
Iterator
이면서Iterable
인 객체를 말한다.
non well-formed
well-formed
이터러블이 진행된 지점을 기억하지 못해 처음부터 Restart 이터러블이 진행된 지점을 기억하고 그 지점부터 Restart
const iterable = {
[Symbol.iterator]() {
let i = 3;
return {
next() {
return i === 0 ? { done: true } : { value: i--, done: false };
},
[Symbol.iterator]() {
return this;
},
};
},
};
let iterator = iterable[Symbol.iterator]();
console.log(iterator[Symbol.iterator]() === iterator); //true
iterator.next();
for (const a of iterator) {
console.log(a);
}
//2
//1
well-formed
로 다시 구현해 보았다Symbol.iterator
를 가지고 있다.
Array
Set
Map
뿐만 아니라DOM
도 이터러블/이터레이터 프로토콜 규약을 따른다.const all = document.querySelectorAll("*"); console.log(all[Symbol.iterator]); //ƒ values() { [native code] } console.log(all[Symbol.iterator]()); //Array Iterator {} let iter = all[Symbol.iterator](); console.log(iter.next()); //{value: html.focus-outline-visible, done: false} console.log(iter.next()); //{value: head, done: false} console.log(iter.next()); //{value: meta, done: false}
const a = [1, 2];
const map = new Map([
["a", 1],
["b", 2],
]);
const b = [...a, ...map];
console.log(b); //[ 1, 2, [ 'a', 1 ], [ 'b', 2 ] ]
for (const a of b) {
console.log(a);
}
//1
//2
//[ 'a', 1 ]
//[ 'b', 2 ]
전개 연산자
로 다른 유형의 값을을 전개한 결과도 이터러블/이터레이터 프로토콜 규약을 따른다.