JS - iterable 친해지기
전개연산자, 제너레이터.. 기존에 알던 개념이지만 이터러블의 관점에서는 생각해본적이 없는거 같아 정리하는 메모.
이터러블/이터레이터 프로토콜
이터러블을 for of, 전개연산자 등에 동작하도록 하는 규약
- 이터러블: 이터레이터를 리턴하는 Symbol.iterator 속성을 가진 객체
- 이터레이터: {value, done} 형태의 객체를 리턴하는 next() 메서드
Well-formed 이터러블
이터레이터가 자기 자신을 반환하는 [Symbol.iterator]를 가진 경우
const iterableSample = {
[Symbol.iterator]() {
let i = 5;
return {
next() {
return i === 0 ? { done: true } : { value: i--, done: false };
},
[Symbol.iterator]() {
return this;
},
};
},
};
const iterator = iterableSample[Symbol.iterator]();
const iterator2 = iterableSample[Symbol.iterator]();
console.log(iterator.next()); // { value: 5, done: false }
console.log(iterator2.next()); // { value: 5, done: false }
for (const value of iterableSample) {
console.log(value); // 5 4 3 2 1
}
console.log(iterator.next()); // { value: 5, done: false }
console.log(iterator.next()); // { value: 4, done: false }
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 }
전개연산자
let arr1 = [1, 2, 3, 4, 5];
let tempIter = arr1[Symbol.iterator]();
tempIter.next(); // { value: 1, done: false }
for (let i of arr1) {
console.log(i); // 1, 2, 3, 4, 5
}
console.log(...arr1); // 1 2 3 4 5
arr1[Symbol.iterator] = null;
console.log(...arr1); // error
제너레이터
이터레이터이자 이터러블을 생성하는 함수, 🧨 순회하는 값을 만들 수가 있다.
function* gen() {
yield 1;
yield 2;
yield 3;
return 99;
}
let iterator = gen();
console.log(iterator[Symbol.iterator] === iterator); // false
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(gen().next()); // { value: 1, done: false }
for (const item of gen()) {
console.log(item); // 1 2 3
}
map, filter, reduce 샘플코드
Array의 내장 메소드이지만 아래와 같이 작성할 수 있고 이럴 경우 다형성이 커진다.
// map
const map = (iter, f) => {
let res = [];
for (const item of iter) {
res.push(f(item));
}
return res;
};
console.log(map(document.querySelectorAll("*"), (el) => el.nodeName));
console.log(document.querySelectorAll("*").map((el) => el.nodeNAme)); // 오류
// filter
const filter = (iter, f) => {
let res = [];
for (const item of iter) {
if (f(item)) {
res.push(item);
}
}
return res;
};
console.log(
filter(document.querySelectorAll("*"), (el) => el.nodeName === "DIV")
);
// reduce
const reduce = (iter, f, init) => {
let res = init || iter[Symbol.iterator]().next().value;
for (const item of iter) {
res = f(res, item);
}
return res;
};
console.log(reduce([1, 2, 3], (a, b) => a + b, 0));
개인이 참고하고자 작성한 글이며, 잘못된 정보가 있을 수 있습니다. 잘못된 정보는 메일로 보내주시면 감사하겠습니다. 🙏