Written by Paul
https://helloworldjavascript.net/pages/260-iteration.html๋ฅผ ์ฐธ์กฐํ์ฌ ์ด ๊ธ์
๋๋ค.
Iterable
iterable obect๋ forโฆof์ ํจ๊ป ES2015์์ ๋์
๋์์ต๋๋ค.
๋ฐ๋ณต ๊ฐ๋ฅํ ๊ฐ์ฒด๋ฅผ ๋ค๋ฅธ ๊ฐ์ฒด์ ๊ตฌ๋ถ์ง๋ ํน์ง์, ๊ฐ์ฒด์ย
Symbol.iterator
ย ์์ฑ์ย ํน๋ณํ ํํ์ ํจ์๊ฐ ๋ค์ด์๋ค๋ ๊ฒ์
๋๋ค.const str = 'hello'; str[Symbol.iterator]; // [Function]
๊ฐ์ฒด์ย
Symbol.iterator
ย ์์ฑ์ ํน์ ํํ์ ํจ์๊ฐ ๋ค์ด์๋ค๋ฉด, ์ด๋ฅผ ๋ฐ๋ณต ๊ฐ๋ฅํ ๊ฐ์ฒด(iterable object) ํน์ ์ค์ฌ์ย iterable์ด๋ผ ๋ถ๋ฅด๊ณ ,ย ํด๋น ๊ฐ์ฒด๋ iterable protocol์ ๋ง์กฑํ๋ค๊ณ ๋งํฉ๋๋ค. ์ด๋ฐ ๊ฐ์ฒด๋ค์ ๋ํด์๋ ES2015์์ ์ถ๊ฐ๋ ๋ค์ํ ๊ธฐ๋ฅ๋ค์ ์ฌ์ฉํ ์ ์์ต๋๋ค.๋ด์ฅ๋ ์์ฑ์ ์ค iterable ๊ฐ์ฒด๋ฅผ ๋ง๋ค์ด๋ด๋ ์์ฑ์์๋ ์๋์ ๊ฐ์ ๊ฒ๋ค์ด ์์ต๋๋ค.
String
Array
TypedArray
Map
Set
Iterable์ ์ฌ์ฉ
์ด๋ค ๊ฐ์ฒด๊ฐ Iterable์ด๋ผ๋ฉด, ๊ทธ ๊ฐ์ฒด์ ๋ํด์ ์๋์ ๊ธฐ๋ฅ๋ค์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
for...of
ย ๋ฃจํ
- spread ์ฐ์ฐ์ (
...
)
- ๋ถํด๋์ (destructuring assignment)
- ๊ธฐํ iterable์ ์ธ์๋ก ๋ฐ๋ ํจ์
์ฆ,ย ๋ฌธ์์ด์ ๋ํด์๋ ์ ๊ธฐ๋ฅ๋ค์ ์ฌ์ฉํ ์ ์์ต๋๋ค.ย ์๋์ ์ฝ๋๋ฅผ ์คํํ๊ณ ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ์ง์ ํ์ธํด๋ณด์ธ์.
// `for...of` for (let c of 'hello') { console.log(c); } // spread ์ฐ์ฐ์ const characters = [...'hello']; // ๋ถํด๋์ const [c1, c2] = 'hello'; // `Array.from`์ iterable ํน์ array-like ๊ฐ์ฒด๋ฅผ ์ธ์๋ก ๋ฐ์ต๋๋ค. Array.from('hello');
ย
Generator ํจ์
Iterable์ ๊ตฌํํ๋ ๊ฐ์ฅ ์ฌ์ด ๋ฐฉ๋ฒ์ ES2015์ ๋์
๋ย generator ํจ์๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์
๋๋ค.
Generator ํจ์๋ย iterable ๊ฐ์ฒด๋ฅผ ๋ฐํํ๋ ํน๋ณํ ํํ์ ํจ์์
๋๋ค.
// ํจ์ ์ ์ธ function* gen1() {} // ํจ์ ํํ์ const gen2 = function* () {} // ๋ฉ์๋ ๋ฌธ๋ฒ const obj = { * gen3() {} }
Generator ํจ์๋ฅผ ํธ์ถํ๋ฉด ๊ฐ์ฒด๊ฐ ์์ฑ๋๋๋ฐ, ์ด ๊ฐ์ฒด๋ iterable protocol์ ๋ง์กฑํฉ๋๋ค. ์ฆ,ย
Symbol.iterator
ย ์์ฑ์ ๊ฐ๊ณ ์์ต๋๋ค.function* gen1() {} const iterable = gen1() iterable[Symbol.iterator] // [Function]
Generator ํจ์ ์์์๋ย
yield
๋ผ๋ ํน๋ณํ ํค์๋๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค. Generator ํจ์ ์์์ย yield
ย ํค์๋๋ย return
๊ณผ ์ ์ฌํ ์ญํ ์ ํ๋ฉฐ, iterable์ ๊ธฐ๋ฅ์ ์ฌ์ฉํ ๋ย yield
ย ํค์๋ ๋ค์ ์๋ ๊ฐ๋ค์ ์์๋๋ก ๋๊ฒจ์ค๋๋ค.function* numberGen() { yield 1; yield 2; yield 3; } // 1, 2, 3์ด ์์๋๋ก ์ถ๋ ฅ๋ฉ๋๋ค. for (let n of numberGen()) { console.log(n); }
yield*
ย ํํ์์ ์ฌ์ฉํ๋ฉด, ๋ค๋ฅธ generator ํจ์์์ ๋๊ฒจ์ค ๊ฐ์ ๋์ ๋๊ฒจ์ค ์๋ ์์ต๋๋ค.function* numberGen() { yield 1; yield 2; yield 3; } function* numberGen2() { yield* numberGen(); yield* numberGen(); } // 1, 2, 3, 1, 2, 3์ด ์์๋๋ก ์ถ๋ ฅ๋ฉ๋๋ค. for (let n of numberGen2()) { console.log(n); }
Generator ํจ์๋ฅผ ์ฌ์ฉํ ๋ ์ฃผ์ํ ์ ์ด ์์ต๋๋ค.
- Generator ํจ์๋ก๋ถํฐ ์์ฑ๋ iterable์ ํ ๋ฒ๋ง ์ฌ์ฉ๋ ์ ์์ต๋๋ค.
- Generator ํจ์ ๋ด๋ถ์์ ์ ์๋ ์ผ๋ฐ ํจ์์์๋ย
yield
ย ํค์๋๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
// Generator ํจ์๋ก๋ถํฐ ์์ฑ๋ iterable์ ํ ๋ฒ๋ง ์ฌ์ฉ๋ ์ ์์ต๋๋ค. function* gen() { yield 1; yield 2; yield 3; } const iter = gen(); for (let n of iter) { // ์ ์ถ๋ ฅ๋ฉ๋๋ค. console.log(n); } for (let n of iter) { // `iter`๋ ํ ๋ฒ ์ฌ์ฉ๋์์ผ๋ฏ๋ก, ์ด ์ฝ๋๋ ์คํ๋์ง ์์ต๋๋ค. console.log(n); }
// Generator ํจ์ ๋ด๋ถ์์ ์ ์๋ ์ผ๋ฐ ํจ์์์๋ `yield` ํค์๋๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค. function* gen2() { // ์์ ๋ฌธ๋ฒ ์ค๋ฅ๊ฐ ๋ฉ๋๋ค. (Unexpected token) function fakeGen() { yield 1; yield 2; yield 3; } fakeGen(); }
ย
Iterator Protocol
Iterable๊ณผ Iterator๋ฅผ ์ ๊ตฌ๋ถํด์ผ ํฉ๋๋ค.
iterable ๊ฐ์ฒด๋ iterable protocol์ ๋ง์ขํ๋ค. ์ฆ, Symbol.Iterator ์์ฑ์ ํน๋ณํ ํํ์ ํจ์๊ฐ ์ ์ฅ๋์ด ์๋ค.
Iterable Protocol์ ๋ง์กฑํ๋ ค๋ฉด, Symbol.iterator ์์ฑ์ ์ ์ฅ๋์ด ์๋ ํจ์๋ iterator ๊ฐ์ฒด๋ฅผ ๋ฐํํด์ผ ํฉ๋๋ค.
iterator ๊ฐ์ฒด๋ ์๋์ ํน๋ณํ ์กฐ๊ฑด์ ๋ง์กฑํ๋ ๊ฐ์ฒด์
๋๋ค.
- iterator๋ next๋ผ๋ ๋ฉ์๋๋ฅผ ๊ฐ์ต๋๋ค.
- next ๋ฉ์๋๋ ๋ค์ ๋ ์์ฑ์ ๊ฐ๋ ๊ฐ์ฒด๋ฅผ ๋ฐํํด์ผ ํฉ๋๋ค.
done - ๋ฐ๋ณต์ด ๋ชจ๋ ๋๋ฌ๋์ง๋ฅผ ๋ํ๋
๋๋ค.
value - ํ์ฌ ์์์ ๊ฐ์ ๋ํ๋
๋๋ค.
์ ์กฐ๊ฑด์ iterator protocol ์ด๋ผ๊ณ ํฉ๋๋ค.
const strIterator = 'go'[Symbol.iterator](); strIterator.next(); // { value: 'g', done: false } strIterator.next(); // { value: 'o', done: false } strIterator.next(); // { value: undefined, done: true } strIterator.next(); // { value: undefined, done: true } function* gen() { yield 1; yield 2; } const genIterator = gen()[Symbol.iterator](); genIterator.next(); // { value: 1, done: false } genIterator.next(); // { value: 2, done: false } genIterator.next(); // { value: undefined, done: true } genIterator.next(); // { value: undefined, done: true }
iterable์ ๋ง๋ค์ด ๋ณด๊ธฐ.
function range(start = 0, end = Infinity, step = 1) { return { currentValue: start, [Symbol.iterator]() { return { next: () => { if (this.currentValue < end) { const value = this.currentValue; this.currentValue += step; return { done: false, value } } else { return { done: true } } } } } } }
ย
Generator์ Iterator
Generator ํจ์๋ก๋ถํฐ ๋ง๋ค์ด์ง ๊ฐ์ฒด๋ ์ผ๋ฐ์ ์ธ iterable์ฒ๋ผ ์ธ ์ ์์ง๋ง, iterator์ ๊ด๋ จ๋ ํน๋ณํ ์ฑ์ง์ ๊ฐ๊ณ ์์ต๋๋ค.
generator ํจ์๋ก๋ถํฐ ๋ง๋ค์ด์ง ๊ฐ์ฒด๋ iterable protocol๊ณผ iterator protocol์ ๋์์ ๋ง์กฑํฉ๋๋ค.
function* gen() { // ... } const genObj = gen(); genObj[Symbol.iterator]().next === genObj.next; // true
Symbol.iterator๋ฅผ ํตํด iterator๋ฅผ ์์ฑํ์ง ์๊ณ ๋ ๋ฐ๋ก next๋ฅผ ํธ์ถ ํ ์ ์์ต๋๋ค.
function* gen() { yield 1; yield 2; yield 3; } const iter = gen(); iter.next(); // { value: 1, done: false } iter.next(); // { value: 2, done: false } iter.next(); // { value: 3, done: false } iter.next(); // { value: undefined, done: true }
ย
generator ํจ์ ์์์ return ํค์๋๋ฅผ ์ฌ์ฉํ๋ฉด ๋ฐ๋ณต์ด ๋ฐ๋ก ๋๋๋ฉด์ next ๋ฉ์๋์์ ๋ฐํ๋๋ ๊ฐ์ฒด์ ์์ฑ์ ์์ ๋ฐํ๊ฐ์ด ์ ์ฅ๋ฉ๋๋ค. ๋ค๋ง, return์ ํตํด ๋ฐํ๋ ๊ฐ์ด ๋ฐ๋ณต ์ ์ฐจ์ ํฌํจ๋์ง๋ ์์ต๋๋ค.
function* gen() { yield 1; return 2; // generator ํจ์๋ ์ฌ๊ธฐ์ ์ข ๋ฃ๋ฉ๋๋ค. yield 3; } const iter = gen(); iter.next(); // { value: 1, done: false } iter.next(); // { value: 2, done: true } iter.next(); // { value: undefined, done: true } // `1`๋ง ์ถ๋ ฅ๋ฉ๋๋ค. for (let v of gen()) { console.log(v); }
ย
generator ํจ์๋ก๋ถํฐ ์์ฑ๋ ๊ฐ์ฒด์ next ๋ฉ์๋์ ์ธ์๋ฅผ ์ฃผ์ด์ ํธ์ถํ๋ฉด, generator ํจ์๊ฐ ๋ฉ์ท๋ ๋ถ๋ถ์ yield ํํ์์ ๊ฒฐ๊ณผ๊ฐ์ ์์์ ๋ฐ์ ์ธ์๊ฐ ๋ฉ๋๋ค.
function* gen() { const received = yield 1; console.log(received); } const iter = gen(); iter.next(); // { value: 1, done: false } // 'hello'๊ฐ ์ถ๋ ฅ๋ฉ๋๋ค. iter.next('hello'); // { value: undefined, done: true }
ย
Generator Examples
// ๊ฐ ํญ๋ชฉ์ ๋ณํํ ํ ๋๊ฒจ์ฃผ๊ธฐ function* map(iterable, mapper) { for (let item of iterable) { yield mapper(item); } } // ๊ฐ ์์๊น์ง์ ๋์ ๊ฐ์ ๋๊ฒจ์ฃผ๊ธฐ function* reduce(iterable, reducer, initial) { let acc = initial; for (let item of iterable) { acc = reducer(acc, item); yield acc; } } // ์กฐ๊ฑด์ ๋ง์กฑํ๋ ํญ๋ชฉ๋ง ๋๊ฒจ์ฃผ๊ธฐ function* filter(iterable, predicate) { for (let item of iterable) { if (predicate(item)) { yield item; } } } // ์ฌ๋ฌ iterable์ ์ฐ๊ฒฐํ๊ธฐ function* concat(iterables) { for (let iterable of iterables) { yield* iterable; } } // ์์ชฝ ๋ช ๊ฐ์ ํญ๋ชฉ๋ง ๋๊ฒจ์ฃผ๊ธฐ function* take(iterable, count = Infinity) { const iterator = iterable[Symbol.iterator](); for (let i = 0; i < count; i++) { // `yield*`์๋ ๋ค๋ฅด๊ฒ, iterator์ `next` ๋ฉ์๋๋ฅผ ์ด์ฉํ๋ฉด iterable์ ์ผ๋ถ๋ง ๊ฐ์ ธ์ฌ ์ ์์ต๋๋ค. const {value, done} = iterator.next(); if (done) break; yield value; } }