1. for-in vs. for-of ๐Ÿ‘ฉโ€๐Ÿ’ป

1. Why JavaScript have two for-loops?

Java ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ํ•  ๋•Œ ์ปดํŒŒ์ผ ๋œ ์ฝ”๋“œ๋ฅผ ๋ดค๋Š”๋ฐ for๋กœ ์ž‘์„ฑํ•œ ์ฝ”๋“œ๊ฐ€ while๋กœ ์ปดํŒŒ์ผ ๋˜๋Š” ๊ฒƒ์„ ๋ณด๊ณ  ๋งค์šฐ ์‹ ๊ธฐํ–ˆ๋˜ ์ ์ด ์žˆ๋‹ค. while์€ ์ฝ”๋“œ ์ž‘์„ฑ ์‹œ์ ์— ๋ฐ˜๋ณต ํšŸ์ˆ˜๋ฅผ ๋ฏธ๋ฆฌ ์•Œ ์ˆ˜ ์—†๊ณ  Runtime ์— ์•Œ ์ˆ˜ ์žˆ๋Š” ๊ฒฝ์šฐ ์‚ฌ์šฉํ•œ๋‹ค๊ณ  ๋ฐฐ์› ์—ˆ๋‹ค.

ํ•˜์ง€๋งŒ ๋Œ€๋ถ€๋ถ„์˜ ์–ธ์–ด๋Š” ์•„์ฃผ ๊ณ ์ „์ ์ธ for-i๋ฅผ ์‚ฌ์šฉํ•˜๋Š” index ๊ธฐ๋ฐ˜์˜ ๋ฐ˜๋ณต๋ฌธ์ด ์•„๋‹Œ Iterable ๊ฐ์ฒด์˜ Elements ๋ฅผ ์ง์ ‘ ๋ฐ˜๋ณต์‹œํ‚ค๋Š” for Syntax ๋ฅผ ์ œ๊ณตํ•˜๋ฉด์„œ do-while๊ณผ ๊ฐ™์€ ๋ฐ˜๋ณต๋ฌธ์ด ์•„๋‹ˆ๋ฉด ์‚ฌ์‹ค์ƒ ๋ฐ˜๋“œ์‹œ while๋กœ ์ž‘์„ฑํ•  ํ•„์š”๊ฐ€ ์—†์–ด์ ธ ์ž‘์„ฑํ•˜๋Š” ์‚ฌ๋žŒ์˜ ์ทจํ–ฅ์— ๋”ฐ๋ฅธ ์„ ํƒ์— ๊ฐ€๊นŒ์›Œ์กŒ๋‹ค.

๊ทธ๋ ‡๋‹ค๋ฉด ํ˜น์‹œ while์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด for์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค ๋” ๋น ๋ฅธ๊ฐ€์— ๋Œ€ํ•œ ๊ถ๊ธˆ์ฆ์ด ์ƒ๊ฒจ ๊ฒ€์ƒ‰์„ ํ•ด๋ณด์•˜๊ณ , ์–ป์–ด๋‚ธ ๊ฒฐ๋ก ์€ ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์ตœ์ ํ™”๋ฅผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ฐจ์ด๋Š” ๋ฏธ๋ฏธํ•˜๊ณ  ์„ฑ๋Šฅ์„ ๋ชฉ์ ์œผ๋กœ ์„ ํƒํ•˜์ง€๋Š” ๋ง๋ผ๋Š” ๊ฒƒ์ด์—ˆ๋‹ค.


JavaScript ๋Š” ๋‹ค๋ฅธ ์–ธ์–ด์™€ ๋‹ฌ๋ฆฌ forEach๋„ ์•„๋‹Œ for ๋ฐ˜๋ณต๋ฌธ์ด 2๊ฐœ๋‚˜ ๋œ๋‹ค. for-await-of์™€ ๊ฐ™์€ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•œ ๋ฐ˜๋ณต๋ฌธ์„ ์ œ์™ธํ•˜๊ณ ๋„ for-of์™€ for-in์ด๋‹ค. ๋Œ€๋ถ€๋ถ„ Monad ๋ฅผ ์ ์šฉํ•˜๊ธฐ ์œ„ํ•ด ๋ฐฐ์—ด์˜ Instance Methods ๋กœ ๊ตฌํ˜„๋œ forEach๋ฅผ ์ œ์™ธํ•˜๋ฉด ํ•˜๋‚˜์˜ for๋งŒ ์กด์žฌํ•œ๋‹ค. ์‹ฌ์ง€์–ด Swift ์˜ ๊ฒฝ์šฐ๋Š” Arrays, Sets ์€ ๋ฌผ๋ก ์ด๊ณ , JavaScript ๋กœ ์น˜๋ฉด Map ๋˜๋Š” Object์™€ ์œ ์‚ฌํ•œ Key-Value ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๋ฅผ ๊ฐ–๋Š” Dictionaries ๋งˆ์ €๋„ for-in ๋ฐ˜๋ณต๋ฌธ ํ•˜๋‚˜๋กœ ๋ชจ๋“  Iterable ๊ฐ์ฒด๋ฅผ ๋Œ๋ฆด ์ˆ˜ ์žˆ๋‹ค.

๊ทธ๋Ÿฐ๋ฐ ์™œ JavaScript ๋Š” for-of์™€ for-in 2๊ฐœ๋‚˜ ์กด์žฌํ•˜๋Š” ๊ฒƒ์ผ๊นŒ?

2. Is for-of faster than for-in?

์„ฑ๋Šฅ์ƒ ์ฐจ์ด๊ฐ€ ์กด์žฌํ• ๊นŒ ๊ถ๊ธˆํ•ด์„œ ์ฐพ์•„๋ณด๋‹ˆ ๋Œ€๋ถ€๋ถ„์˜ ๋ธ”๋กœ๊ทธ์—์„œ for-in์€ ๋‹ค๋ฅธ ๋ฐ˜๋ณต๋ฌธ์— ๋น„ํ•ด ๋Š๋ฆฌ๋‹ค๊ณ  ์ด์•ผ๊ธฐํ•œ๋‹ค. ๋ฌผ๋ก , Monad ๋ฅผ ์ ์šฉํ•˜๊ธฐ ์œ„ํ•œ ๋ฐฐ์—ด ๋ฉ”์„œ๋“œ์ธ forEach, map, filter ์™€ ๊ฐ™์€ ๋ฐ˜๋ณต์€ ๊ธฐ๋ณธ์ ์ธ for, while์— ๋น„ํ•˜๋ฉด ์กฐ๊ธˆ ๋Š๋ฆฌ์ง€๋งŒ ๊ฐ€์ ธ๋‹ค ์ฃผ๋Š” ์ด์ ์ด ๋”์šฑ ํฌ๊ธฐ ๋•Œ๋ฌธ์— ๋น„๊ต ๋Œ€์ƒ์ด ์•„๋‹ˆ๋‹ค.

๋ฐฐ์—ด ๋ฉ”์„œ๋“œ๋„ ์•„๋‹Œ๋ฐ ์™œ ๋” ๋Š๋ฆฐ for-in ๋ฐ˜๋ณต๋ฌธ์€ deprecated ๊ฐ€ ๋˜์ง€ ์•Š๋Š”๊ฑธ๊นŒ?

for-of ์™€ for-in ์˜ ์ •์˜์™€ ์ฐจ์ด์ ์„ ์‚ดํŽด๋ณด๋ฉฐ ์ด์•ผ๊ธฐ ํ•˜๊ฒ ์ง€๋งŒ, ๊ฒฐ๋ก ๋ถ€ํ„ฐ ๋งํ•˜์ž๋ฉด ๋‘˜์€ ์• ์ดˆ์— ์šฉ๋„๊ฐ€ ๋‹ค๋ฅด๋‹ค. ๋ฐฐ์—ด์— for-in ๋ฅผ ์‚ฌ์šฉํ•ด๋†“๊ณ  ๋Š๋ฆฌ๋‹ค๊ณ  ํ•˜๋Š” ๊ฒƒ์€ MDN - forโ€ฆin ์˜ ์„ค๋ช…์„ ์ œ๋Œ€๋กœ ๋ณด์ง€ ์•Š๊ณ  ์‚ฌ์šฉํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์ด๊ฒƒ์€ ์ข€ ๋” ์ •ํ™•ํžˆ ๋งํ•˜๋ฉด ์ž˜๋ชป ์‚ฌ์šฉํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋Š๋ฆฐ ๊ฒƒ์ด๋‹ค.

3. for-of

์–ด๋–ค ์–ธ์–ด์—์„œ๋“  ๋ฐฐ์—ด์€ index ๊ธฐ๋ฐ˜์ด๊ณ , ๋ฐฐ์—ด์ด๋ผ๋Š” ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ๊ฐ–๊ณ  ์žˆ๋Š” ๊ฐ๊ฐ์˜ ์•„์ดํ…œ์„ ๋ฝ‘์•„์„œ ๋Œ๋ฆฌ๋Š” ๋ฐ˜๋ณต๋ฌธ์ด ์กด์žฌํ•˜๊ธฐ ์ด์ „์— ๋ฐ˜๋ณต์„ ๋Œ๋ฆฌ๋Š” ๋ฐฉ๋ฒ•์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

const fruits = ['Apple', 'Banana', 'Cherry'];

for (let i = 0; i < fruits.length; i++) {
  console.log(fruits[i]);
}

๋ฐฐ์—ด์˜ ๊ธธ์ด๋ฅผ ๊ตฌํ•˜๊ณ , ์ธ๋ฑ์Šค๋กœ ์‚ฌ์šฉํ•  ๋ณ€์ˆ˜๋ฅผ ์ƒ์„ฑํ•œ ๋‹ค์Œ, ๊ทธ ์ธ๋ฑ์Šค๋กœ ๋ฐฐ์—ด์— ์ ‘๊ทผํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ์ด๋ฅผ for-i ๋ฐ˜๋ณต๋ฌธ์ด๋ผ ํ•˜์ž. ๊ทธ๋Ÿฐ๋ฐ ์–ด์ฐจํ”ผ ๋ฐฐ์—ด์„ ๋Œ๋ฆฌ๋Š” ๋ชฉ์  ์ž์ฒด๊ฐ€ ๋ฐฐ์—ด์˜ ์•„์ดํ…œ์„ ๊ฐ€์ง€๊ณ  ๋ฌด์–ธ๊ฐ€๋ฅผ ํ•˜๊ธฐ ์œ„ํ•จ์ด์ง€ ์•Š์€๊ฐ€? ๊ทธ๋ž˜์„œ ๋งŽ์€ ์–ธ์–ด๋“ค์€ ๋ฐฐ์—ด์˜ ํƒ€์ž…์— ์ˆœํ™˜ ๊ฐ€๋Šฅํ•œ ์–ด๋–ค ํ”„๋กœํ† ์ฝœ์„ ๋”ฐ๋ฅด๋„๋ก ์ ํ•ฉ์„ฑ์„ ์ถ”๊ฐ€ํ•˜๊ณ , ์ด๊ฑธ ์ด์šฉํ•ด ๋ฐ”๋กœ ์•„์ดํ…œ์„ ๊บผ๋‚ด์˜ค๋„๋ก ํ•˜๋Š” ๋ฐ˜๋ณต๋ฌธ์ด ์ƒ๊ฒผ๋‹ค. JavaScript ์—์„  ์ด๊ฒƒ์ด ๋ฐ”๋กœ for-of๋‹ค.

MDN - forโ€ฆof ๋ฅผ ๋ณด๋ฉด Iterable Objects๋ฅผ ๋ฐ˜๋ณตํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ์„ค๋ช…ํ•œ๋‹ค. ์ด๊ฒƒ์€ ๋‹ค๋ฅธ ์–ธ์–ด์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ Iterable ํ”„๋กœํ† ์ฝœ์„ ์ค€์ˆ˜ํ•˜๋Š” ๋ฐ์ดํ„ฐ ํƒ€์ž…์„ ์˜๋ฏธํ•œ๋‹ค. JavaScript ์—์„œ ์ด๊ฒƒ์€ Iteration Protocols ๋ฅผ ์ค€์ˆ˜ํ•˜๋Š” Types ๋ฅผ ์˜๋ฏธํ•˜๋ฉฐ, ์ด๊ฒƒ์€ ๋‘ ๊ฐœ์˜ ํ”„๋กœํ† ์ฝœ์„ ์˜๋ฏธํ•œ๋‹ค.

Iteration Protocols

  • Iterable Protocol: @@iterator ์‹ฌ๋ณผ๋กœ ์•Œ๋ ค์ง„ [Symbol.iterator] ๋ฉ”์„œ๋“œ๋ฅผ ๊ตฌํ˜„ํ•œ ๋ฐ์ดํ„ฐ ํƒ€์ž…์œผ๋กœ Array, Map ๋“ฑ์ด ํ•ด๋‹นํ•œ๋‹ค.
  • Iterator Protocol: Iterator.prototype์„ ์ƒ์†ํ•ด this๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” [@@iterator]() ๋ฉ”์„œ๋“œ๋ฅผ ๊ตฌํ˜„ํ•œ ๋ฐ์ดํ„ฐ ํƒ€์ž…์œผ๋กœ Generator Object ๊ฐ€ ํ•ด๋‹นํ•œ๋‹ค.

๋ชจ๋“  ๊ฐ์ฒด๋Š” Iteration Protocols ๋ฅผ ๋”ฐ๋ฅด๋„๋ก ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๋‹ค์Œ์€ Iterable Protocol ๊ณผ Iterator Protocol ์„ ๋ชจ๋‘ ๋งŒ์กฑ์‹œํ‚ค๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ ๋‘ ๋ฉ”์„œ๋“œ๋ฅผ ๊ตฌํ˜„ํ•˜๋ฉด ๋œ๋‹ค.

// Satisfies both the Iterator Protocol and Iterable
const myIterator = {
  next() {
    // ...
  },
  [Symbol.iterator]() {
    return this;
  },
};

์ด ๋‘ ๋ฉ”์„œ๋“œ๋ฅผ ๊ตฌํ˜„ํ•˜๋ฉด ๊ทธ ๊ฐ์ฒด๋Š” Iteration Protocols๋ฅผ ์ค€์ˆ˜ํ•˜๊ฒŒ ๋œ๋‹ค.
JavaScript ์—์„œ Iteration Protocols ๋ฅผ ์ค€์ˆ˜ํ•˜๋Š” Iterable Objects ๋Š” Array, String, TypedArray, Map, Set, NodeList(and other DOM collections), arguments, Generator Functions, User-Defined Iterablesโ€ฆ ์„ ์˜๋ฏธํ•œ๋‹ค.

์ด๊ฒƒ์ด ๋ฐ”๋กœ ์ผ๋ฐ˜์ ์œผ๋กœ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด์—์„œ for ๋ฐ˜๋ณต๋ฌธ์„ ๋Œ๋ฆฌ๋Š” ๋ฐฉ๋ฒ•์ด๊ณ , JavaScript ์˜ for ๋ฐ˜๋ณต๋ฌธ์„ ๋Œ๋ฆฌ๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.


Iteration Protocols๋ฅผ ์ค€์ˆ˜ํ•˜๋Š” ๋ฐ์ดํ„ฐ Types ๋Š” for-of ๋ฐ˜๋ณต๋ฌธ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

for (const fruit of fruits) {
  console.log(fruit);
}

4. for-in

for-of ์—์„œ ์•Œ ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์€, Map, Set์€ for-of๋ฅผ ์‚ฌ์šฉํ•ด ๋ฐ˜๋ณต์„ ๋Œ๋ฆด ์ˆ˜ ์žˆ๋Š”๋ฐ, ์—ฌ๊ธฐ์— Object๊ฐ€ ๋น ์กŒ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค. JavaScript ๋Š” ํ•จ์ˆ˜๋„ Function Objects ์ผ ์ •๋„๋กœ ๋ชจ๋“  ๊ฒƒ์ด Object ๊ธฐ๋ฐ˜์ด๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์ •์ž‘ ๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉํ•˜๋Š”
Object๋Š” Iteration Protocols ๋ฅผ ๋”ฐ๋ฅด์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

const fruits = {
  Apple: 4200,
  Banana: 6800,
  Cherry: 3400,
};

for (const fruit of fruits) {   // Uncaught TypeError: fruits is not iterable
  console.log(fruit);
}


๊ทธ๋ ‡๋‹ค๋ฉด Object๋Š” ์–ด๋–ป๊ฒŒ ๋ฐ˜๋ณต์„ ๋Œ๋ ค์•ผ ํ• ๊นŒ? for-i ๋ฐ˜๋ณต๋ฌธ์„ ์‚ฌ์šฉํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค.

for (let i = 0, keys = Object.keys(fruits); i < keys.length; i++) {
  console.log(keys[i], fruits[keys[i]]);
}

Object์˜ prototype ์˜ keys ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด ๋ชจ๋“  ํ‚ค๋ฅผ ์ถ”์ถœํ•ด์„œ ๋ฐฐ์—ด๋กœ ๋งŒ๋“ค๊ณ , ์ด๊ฒƒ์„ ์ด์šฉํ•ด ์ ‘๊ทผํ•˜๋Š” ๊ฒƒ์ด๋‹ค.


for-i๋ณด๋‹ค for-of ๊ฐ€ ๊ฐ€๋…์„ฑ์€ ๋ฌผ๋ก ์ด๊ณ  ์‚ฌ์šฉํ•˜๊ธฐ ๋” ํŽธํ•œ ๊ฒƒ ๊ฐ™์€๋ฐ ๋ฐฉ๋ฒ•์ด ์—†์„๊นŒ?

for (const [key, value] of Object.entries(fruits)) {
  console.log(key, value);
}

Object์˜ prototype ์˜ entries๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด, [key, value] Tuple ํƒ€์ž…์˜ ๋ฐฐ์—ด์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค(์—„๋ฐ€ํžˆ ๋งํ•˜๋ฉด TypeScript ๊ฐ€ ์•„๋‹Œ JavaScript ์—์„œ ์ด๊ฒƒ์€ ๋ฐฐ์—ด์ด๊ณ , ์ด์ค‘ ๋ฐฐ์—ด์— ํ•ด๋‹นํ•œ๋‹ค). ์ด๊ฒƒ์„ ์ด์šฉํ•ด ์ ‘๊ทผํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

๋ฌผ๋ก , forEach๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด

Object.keys(fruits).forEach((key) => console.log(key, fruits[key]));
Object.values(fruits).forEach((value) => console.log(value));
Object.entries(fruits).forEach(([key, value] = user) => console.log(key, value));

์™€ ๊ฐ™์ด ์ ‘๊ทผํ•  ์ˆ˜๋„ ์žˆ์ง€๋งŒ, for-of ๋ฅผ ํ†ตํ•œ ์ ‘๊ทผ ๋ฐฉ๋ฒ•๊ณผ ํฌ๊ฒŒ ๋‹ค๋ฅด์ง€ ์•Š์„ ๋ฟ๋”๋Ÿฌ, break์™€ ๊ฐ™์€ Control Flow ๋ฅผ ํ•  ์ˆ˜ ์—†๋‹ค๋Š” ๋ฌธ์ œ๊ฐ€ ์žˆ๋‹ค.

๊ทธ๋ž˜์„œ ๋‚˜์˜จ ๊ฒƒ์ด for-in ๋ฐ˜๋ณต๋ฌธ์ด๋‹ค. MDN - forโ€ฆin ๋ฅผ ๋ณด๋ฉด Symbols๋ฅผ ์ œ์™ธํ•œ ๋ชจ๋“  enumerable string properties ๋ฅผ ๋ฐ˜๋ณตํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ์„ค๋ช…ํ•œ๋‹ค.

์ด ์—ด๊ฑฐ ๊ฐ€๋Šฅํ•œ ๋ฌธ์ž์—ด ์†์„ฑ(enumerable string properties)์ด๋ž€ ๋‹ค์Œ์„ ์˜๋ฏธํ•œ๋‹ค.

const obj = {};

Object.defineProperty(obj, 'Apple', {
  value: 4200,
  enumerable: true  // default, true
});

Object.keys, Object.values, Object.entries ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ฌธ์ž์—ด properties ๋ฅผ ์˜๋ฏธํ•œ๋‹ค. ๊ฒฐ๊ตญ Object๋ฅผ ์œ„ํ•ด ์กด์žฌํ•˜๋Š” ๋ฐ˜๋ณต๋ฌธ์ด๋ผ๋Š” ๋ง์ด๋‹ค.

for (const fruit in fruits) {
  console.log(fruit, fruits[fruit]);
}

5. What is the problem?

Iteration Protocols๋ฅผ ์ค€์ˆ˜ํ•˜๋Š” Types ๋Š” for-of ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ , ์ด๋ฅผ ๋”ฐ๋ฅด์ง€ ์•Š๋Š” Object๋Š” for-in ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ๋ฌด์—‡์ด ๋ฌธ์ œ์ผ๊นŒ? ์™œ ๋…ผ๋ž€์ด ์žˆ๋Š” ๊ฒƒ์ผ๊นŒ?

์ด๊ฒƒ์€ JavaScript ์˜ ๊ตฌ์กฐ์ ์ธ ๋ฌธ์ œ ๋•Œ๋ฌธ์ด๋‹ค. JavaScript ๋Š” ๋‹ค๋ฅธ ์–ธ์–ด์™€ ๋‹ฌ๋ฆฌ Arrays ์—ญ์‹œ Objects ์ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

const fruits = ['Apple', 'Banana', 'Cherry'];

for (const fruit in fruits) {
  console.log(fruit, fruits[fruit]);
}

์ฆ‰, Objects ๋ฅผ ์œ„ํ•ด ์กด์žฌํ•˜๋Š” ๋ฐ˜๋ณต๋ฌธ์ธ๋ฐ, Arrays ์— ์‹คํ–‰์ด ๊ฐ€๋Šฅํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค(์ฐธ๊ณ ๋กœ Map ์€ ๋™์ผํ•˜๊ฒŒ Key-Value Types ์ง€๋งŒ Iteration Protocols๋ฅผ ์ค€์ˆ˜ํ•˜๊ธฐ ๋•Œ๋ฌธ์— for-of ๋ฐ˜๋ณต๋ฌธ ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•˜๋‹ค).

ย  for-of(Iteration Protocols) for-in(enumerable string properties)
Objects X O
Arrays O O
Map O X

ํ‘œ๋กœ ๋ณด๋ฉด ํ•œ ๋ˆˆ์— ๋ฌด์—‡์ด ๋ฌธ์ œ์ธ์ง€ ์•Œ ์ˆ˜ ์žˆ๋‹ค. Arrays ์— ์‹ค์ œ๋กœ for-of์™€ for-in์ด ๋ชจ๋‘ ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

const fruits = ['Apple', 'Banana', 'Cherry'];

for (const fruit in fruits) {
  console.log(fruit, fruits[fruit]);
}
0 Apple
1 Banana
2 Cherry

๋ฌผ๋ก  ์ด๋Ÿฐ์‹์œผ๋กœ index๋ฅผ Key ๋กœ ํ•˜๋Š” Key-Value Types ํ˜•ํƒœ์˜ Objects ๋กœ ์ธ์‹๋˜์ง€๋งŒ ๋ง์ด๋‹ค.
๋น ๋ฅด๊ฒŒ ๋ฐ˜๋ณตํ•  ์ˆ˜ ์žˆ๋„๋ก ๋งŒ๋“ค์–ด ๋†“์€ ํ”„๋กœํ† ์ฝœ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  ๊ตณ์ด for-in์„ ์‚ฌ์šฉํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋Š๋ฆฐ ๊ฒƒ์ด๋‹ค.


2. Performance Test ๐Ÿ‘ฉโ€๐Ÿ’ป

for-in ์ด ๋Š๋ฆฐ ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ์ž˜๋ชป ์‚ฌ์šฉํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋Š๋ฆฐ ๊ฒƒ์ด๋ผ๊ณ  ์•ž์—์„œ ์„ค๋ช…ํ–ˆ๋‹ค. ๋ฐ˜๋ณต์„ ์œ„ํ•ด Iteration Protocols์„ ๋งŒ๋“ค๊ณ , ์ด๊ฒƒ์„ ์‚ฌ์šฉํ•˜๋Š” for-of๋ฅผ ๋งŒ๋“ค์–ด ๋†จ๋Š”๋ฐ Iteration Protocols๋ฅผ ์ง€์›ํ•˜๋Š” ๋ฐฐ์—ด์— ๊ตณ์ด for-of๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  for-in์„ ์‚ฌ์šฉํ•˜๋‹ˆ ๋Š๋ฆฐ ๊ฒƒ์ด๋‹ค.

๊ทธ๋ ‡๋‹ค๋ฉด for-in ์„ ๋ชฉ์ ์— ๋งž๊ฒŒ Objects์— ์‚ฌ์šฉํ•ด๋„ ์ •๋ง๋กœ for-in ๋ณด๋‹ค ๋Š๋ฆด๊นŒ?

๋‹ค์Œ ํ•จ์ˆ˜๋Š” ์ฃผ์–ด์ง„ ๊ธธ์ด์— ๋งž๊ฒŒ properties ๋ฅผ 1๋ถ€ํ„ฐ ์ƒ์„ฑํ•œ๋‹ค.

function LargeObject(length) {
  for (let i = 1; i <= length; i++) {
    this[i] = i;
  }
}

์˜ˆ๋ฅผ ๋“ค์–ด

const obj = new LargeObject(10);

๋Š” {1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9, 10: 10}๋ฅผ ์ƒ์„ฑํ•˜๊ณ , ๋ชจ๋“  values ๋ฅผ ํ•ฉํ•˜๋ฉด 1~10 ์˜ ๋“ฑ์ฐจ์ˆ˜์—ด์˜ ํ•ฉ์„ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค. ์ด๋ฅผ ์ด์šฉํ•ด 1~5000๋งŒ์˜ ๋“ฑ์ฐจ์ˆ˜์—ด์˜ ํ•ฉ์„ ๊ตฌํ•ด๋ณด์ž.

1. for-i

let t1 = performance.now();
let result = 0;
for (let i = 0, keys = Object.keys(obj); i < keys.length; i++) {
  result += obj[keys[i]];
}
console.log(`Sum: ${result}, ${result === 1_250_000_025_000_000}`);
let t2 = performance.now();
console.log(`Time Elapsed: ${(t2 - t1) / 1000} seconds.`);

(13.6785 + 13.9869 + 13.9156 + 13.6352 + 13.5382) / 5 = 13.7509s

2. for-in

let t1 = performance.now();
let result = 0;
for (const key in obj) {
  result += obj[key];
}
console.log(`Sum: ${result}, ${result === 1_250_000_025_000_000}`);
let t2 = performance.now();
console.log(`Time Elapsed: ${(t2 - t1) / 1000} seconds.`);

(16.2790 + 16.1625 + 15.5917 + 16.2225 + 15.9502) / 5 = 16.0412s

3. for-of

  • Object.entries
let t1 = performance.now();
let result = 0;
for (const [key, value] of Object.entries(obj)) {
  result += value;
}
console.log(`Sum: ${result}, ${result === 1_250_000_025_000_000}`);
let t2 = performance.now();
console.log(`Time Elapsed: ${(t2 - t1) / 1000} seconds.`);

97.4734s ์ด๊ฑด ๋ญ ์ถ”๊ฐ€๋กœ ํ™•์ธํ•ด๋ณผ ํ•„์š”๋„ ์—†์„ ์ •๋„๋กœ ๋Š๋ฆฌ๋‹ค. ๊ทธ๋ ‡๋‹ค๋ฉด Object.entries๊ฐ€ ์•„๋‹Œ Object.keys ๋˜๋Š” Object.values๋กœ ์ ‘๊ทผํ•ด๋ณด๋ฉด ์–ด๋–จ๊นŒ?

  • Object.keys
let t1 = performance.now();
let result = 0;
for (const key of Object.keys(obj)) {
  result += obj[key];
}
console.log(`Sum: ${result}, ${result === 1_250_000_025_000_000}`);
let t2 = performance.now();
console.log(`Time Elapsed: ${(t2 - t1) / 1000} seconds.`);

(13.2539 + 13.3760 + 14.1827 + 13.6154 + 13.2166) / 5 = 13.5289s

์‚ฌ์‹ค์ƒ for-i์™€ ๊ฐ™์€ ๋กœ์ง์œผ๋กœ ๋ด๋„ ๋˜๋‹ค๋ณด๋‹ˆ ์„ฑ๋Šฅ ์—ญ์‹œ ๋™์ผํ•˜๊ฒŒ ํ™•์ธ๋œ๋‹ค.

  • Object.values
let t1 = performance.now();
let result = 0;
for (const value of Object.values(obj)) {
  result += value;
}
console.log(`Sum: ${result}, ${result === 1_250_000_025_000_000}`);
let t2 = performance.now();
console.log(`Time Elapsed: ${(t2 - t1) / 1000} seconds.`);

(0.4228 + 0.4267 + 0.4233 + 0.4990 + 0.4860) / 5 = 0.4516s

4. forEach & reduce

  • forEach
let t1 = performance.now();
let result = 0;
Object.keys(obj).forEach((key) => (result += obj[key]));
console.log(`Sum: ${result}, ${result === 1_250_000_025_000_000}`);
let t2 = performance.now();
console.log(`Time Elapsed: ${(t2 - t1) / 1000} seconds.`);

(14.2790 + 13.3194 + 12.9647 + 13.5687 + 13.4019) / 5 = 13.5067s

for-of๋ƒ, for-in์ด๋ƒ ๋ณด๋‹ค๋Š” ์‚ฌ์‹ค์ƒ Object.keys๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š”๊ฐ€? Object.values ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š”๊ฐ€? Object.entries ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š”๊ฐ€๊ฐ€ ์ค‘์š”ํ•˜๋‹ค๋Š” ๊ฒƒ์„ ํ™•์ธํ–ˆ๋‹ค. ๊ทธ๋ ‡๋‹ค๋ฉด ๋‹น์—ฐํ•˜๊ฒŒ๋„ forEach๋ฅผ ์‚ฌ์šฉํ•ด๋„ ์ด ์กฐ๊ฑด์€ ๋™์ผํ•  ๊ฒƒ์ด๋‹ค. ์ด๋ฒˆ์—๋Š” Object.values๋ฅผ ์‚ฌ์šฉํ•ด ๋‹ค์‹œ ๋ฐ˜๋ณต์„ ๋Œ๋ ค๋ณด์ž.

let t1 = performance.now();
let result = 0;
Object.values(obj).forEach((value) => (result += value));
console.log(`Sum: ${result}, ${result === 1_250_000_025_000_000}`);
let t2 = performance.now();
console.log(`Time Elapsed: ${(t2 - t1) / 1000} seconds.`);

(0.6285 + 0.6105 + 0.7024 + 0.7003 + 0.7266) / 5 = 0.6737s

  • reduce

Object.values๋กœ ๋ฐฐ์—ด์„ ๋งŒ๋“  ๋‹ค์Œ for-of๋ฅผ ์‚ฌ์šฉํ•œ ๊ฒƒ๋ณด๋‹ค ๋ฏธ์„ธํ•˜๊ฒŒ ๋Š๋ฆฌ์ง€๋งŒ, ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•๊ณผ๋Š” ๋น„๊ตํ•  ์ˆ˜ ์—†์„ ๋งŒํผ ๋น ๋ฅด๋‹ค. ๊ทธ๋ ‡๋‹ค๋ฉด ์ข€ ๋” ์ˆœ์ˆ˜ ํ•จ์ˆ˜๊ฐ€ ๋˜๋„๋ก ์™ธ๋ถ€์™€์˜ ์ƒํ˜ธ์ž‘์šฉ์„ ์™„์ „ํžˆ ์ฐจ๋‹จ์‹œ์ผœ reduce๋ฅผ ์ ์šฉ์‹œ์ผœ๋ณด์ž.

let t1 = performance.now();
const result = Object.values(obj).reduce((acc, cur) => acc + cur, 0);
console.log(`Sum: ${result}, ${result === 1_250_000_025_000_000}`);
let t2 = performance.now();
console.log(`Time Elapsed: ${(t2 - t1) / 1000} seconds.`);

(0.4886 + 0.4565 + 0.4839 + 0.4827 + 0.5215) / 5 = 0.4866s

Object.values๋กœ ๋ฐฐ์—ด์„ ๋งŒ๋“  ๋‹ค์Œ for-of๋ฅผ ์‚ฌ์šฉํ•œ ๊ฒƒ๊ณผ ์ฐจ์ด๊ฐ€ ์—†๋‹ค.

5. while

let t1 = performance.now();
let result = 0;
let i = 0;
const keys = Object.keys(obj);
while (i < keys.length) {
  result += obj[keys[i]];
  i++;
}
console.log(`Sum: ${result}, ${result === 1_250_000_025_000_000}`);
let t2 = performance.now();
console.log(`Time Elapsed: ${(t2 - t1) / 1000} seconds.`);

(13.5255 + 13.7702 + 13.7690 + 13.2951 + 13.2285) / 5 = 13.5177s

๋งˆ์ฐฌ๊ฐ€์ง€๋กœ Object.values๋ฅผ ์‚ฌ์šฉํ•ด ๋‹ค์‹œ ๋ฐ˜๋ณต์„ ๋Œ๋ ค๋ณด์ž.

let t1 = performance.now();
let result = 0;
let i = 0;
const values = Object.keys(obj);
while (i < values.length) {
  result += values[i];
  i++;
}
console.log(`Sum: ${result}, ${result === 1_250_000_025_000_000}`);
let t2 = performance.now();
console.log(`Time Elapsed: ${(t2 - t1) / 1000} seconds.`);

(0.2336 + 0.1520 + 0.2443 + 0.2490 + 0.2424) / 5 = 0.2243s

์ฐธ๊ณ ๋กœ for-i ์—ญ์‹œ Object.values๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ฐฐ์—ด์„ ๋Œ๋ฆฌ๋ฉด while๊ณผ ๋™์ผํ•œ ์„ฑ๋Šฅ์ด ์ธก์ •๋œ๋‹ค.

6 Summary

ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ๋ฅผ ํ‘œ๋กœ ์ •๋ฆฌํ•ด๋ณด์ž. ์šฐ์„  for-in์„ ์ œ์™ธํ•œ ๋ชจ๋“  ๋ฐฉ๋ฒ•์€ ๋ฐ˜๋ณต์„ ๋Œ๋ฆฌ๊ธฐ ์œ„ํ•ด์„œ๋Š” Object.entries, Object.keys, Object.values ๋ฉ”์„œ๋“œ ์ค‘ ํ•˜๋‚˜๋ฅผ ์‚ฌ์šฉํ•ด์•ผํ•˜๋Š”๋ฐ, ์ด ๋ฉ”์„œ๋“œ์— ๋”ฐ๋ฅธ ํŽธ์ฐจ๊ฐ€ ๋งค์šฐ ํฌ๋‹ค.

for-of
Object.entries Object.keys Object.values
97.4734s 13.5289s 0.4516s

์ด ๊ฒฐ๊ณผ๋งŒ ๋†“๊ณ  ๋ณด๋ฉด ๋ฌด์กฐ๊ฑด Object.values๋ฅผ ์‚ฌ์šฉํ•ด์•ผ๋งŒ ํ•  ๊ฒƒ ๊ฐ™๋‹ค. ํ•˜์ง€๋งŒ ์—ฌ๊ธฐ์—” ์น˜๋ช…์ ์ธ ๋ฌธ์ œ์ ์ด ์กด์žฌํ•œ๋‹ค.

const fruits = {
  Apple: 4200,
  Apricot: 9200,
  Avocado: 10300,
  Banana: 6800,
  Blueberry: 9700,
  Blackberry: 8300,
  Cherry: 3400,
  Clementine: 7700,
  Cranberry: 6900,
  ...
  Grape: 3700,
  Guava: 7100,
  Gooseberry: 8100,
  ...
}

์œ„ fruits ๊ฐ์ฒด์˜ ๋ชจ๋“  ๊ณผ์ผ ๊ฐ€๊ฒฉ์˜ ํ‰๊ท ์„ ๊ตฌํ•ด์•ผ ํ•  ๊ฒฝ์šฐ๋Š” Object.values๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋งค์šฐ ๋น ๋ฅผ ๊ฒƒ์ด๋‹ค. ํ•˜์ง€๋งŒ ์šฐ๋ฆฌ๊ฐ€ Key-Value Types ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ ๋Š”, key ์™€ value ์˜ ์—ฐ๊ด€์„ฑ์ด ํ•„์š”ํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๋งŒ์•ฝ ์•ŒํŒŒ๋ฒณ โ€˜Bโ€™๋กœ ์‹œ์ž‘ํ•˜๋Š” ๊ณผ์ผ์˜ ํ‰๊ท  ๊ฐ€๊ฒฉ์„ ๊ตฌํ•ด์•ผ ํ•  ๊ฒฝ์šฐ์™€ ๊ฐ™์€ ์ƒํ™ฉ์—์„œ Object.values๋Š” ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค. ๋ชจ๋“  ๊ฐ’์„ ์ผ๋‹จ ๋ฐฐ์—ด๋กœ ๋งŒ๋“ค๊ณ  ๋ฐ˜๋ณต์„ ๋Œ๋ฆฌ๊ธฐ ๋•Œ๋ฌธ์— for-of๋Š” ๋ฌผ๋ก ์ด๊ณ , for-i, while ์™€ ๊ฐ™์€ ๋ชจ๋“  ๋ฐ˜๋ณต๋ฌธ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ณ  ๋น ๋ฅด์ง€๋งŒ ์ด๊ฑด ๊ทธ์ € Values ์˜ ๋ฐฐ์—ด์„ ์ถ”์ถœํ•œ ๋‹ค์Œ ๋ฐฐ์—ด์„ ๋ฐ˜๋ณต๋ฌธ ๋Œ๋ฆฌ๋Š” ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์ด๊ฒƒ์€ ์šฐ๋ฆฌ๊ฐ€ ์›ํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋‹ค.

for-i (Object.keys) 13.7509s
for-in 16.0412s
for-of (Object.keys) 13.5289s
forEach (Object.keys) 13.5067s
while (Object.keys) 13.5177s

key๊ฐ€ ํ•„์š”ํ•œ ์ƒํ™ฉ์—์„œ ์—ญ์‹œ for-in์ด ์กฐ๊ธˆ ๋Š๋ฆฌ๊ธด ํ•˜๋‹ค. ํ•˜์ง€๋งŒ ์ด๊ฒƒ์€ ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•ด ํ•œ ๊ฐ์ฒด ์•ˆ์— properties ๋ฅผ 5000๋งŒ๊ฐœ๋‚˜ ์ƒ์„ฑํ•œ ๊ทน๋‹จ์ ์ธ ๊ฒฝ์šฐ๋ผ๋Š” ๊ฒƒ์„ ๊ณ ๋ คํ•ด์•ผํ•œ๋‹ค. ๋ฐฐ์—ด๋„ ์•„๋‹Œ ๋‹จ์ผ ๊ฐ์ฒด ๋‚ด์— properties ๋ฅผ 5000๋งŒ๊ฐœ๋‚˜ ๋งŒ๋“œ๋Š” ๊ฒฝ์šฐ๋Š” ์—†๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ํ˜น์‹œ๋ผ๋„ ์ด๋Ÿฐ ์ƒํ™ฉ์ด ์กด์žฌํ•œ๋‹ค๋ฉด, ์„ค๊ณ„๋ถ€ํ„ฐ ์‹ฌ๊ฐํ•˜๊ฒŒ ์ž˜๋ชป ๋˜์ง„ ์•Š์•˜๋‚˜๋ฅผ ๊ณ ๋ฏผํ•ด๋ด์•ผ ํ•œ๋‹ค. ์ด๋ ‡๊ฒŒ ๊ทน๋‹จ์ ์ธ ๊ฒฝ์šฐ์— ์กฐ์ฐจ 18% ์ •๋„ ์ฐจ์ด์ผ ๋ฟ ์•„๋‹ˆ๋ผ ์ ˆ๋Œ€์  ์ˆ˜์น˜๋กœ๋„ 1.5์ดˆ ์ •๋„ ์ฐจ์ด๋‹ค.

๊ฒฐ๋ก ์ ์œผ๋กœ ๋งํ•˜์ž๋ฉด ํ†ต์ƒ์ ์ธ ๋ฒ”์œ„ ๋‚ด์—์„œ Object ๋ฅผ ๋ฐ˜๋ณตํ•  ๋•Œ for-of์™€ for-in์˜ ์†๋„ ์ฐจ์ด๋Š” ์—†๋‹ค๊ณ  ํ•  ์ˆ˜ ์žˆ๋‹ค.


3. Final Conclusion ๐Ÿ‘ฉโ€๐Ÿ’ป

  1. Arrays ์™€ ๊ฐ™์ด Iteration Protocols ๋ฅผ ์ง€์›ํ•˜๋Š” Types ๋Š” for-of ๋˜๋Š” forEach๋ฅผ ์‚ฌ์šฉํ•ด๋ผ(๋ฐฐ์—ด์ด ๋งค์šฐ ํด ๊ฒฝ์šฐ๋Š” for-i์™€ while์ด for-of๋‚˜ forEach๋ณด๋‹ค ์กฐ๊ธˆ ๋น ๋ฅด๊ธด ํ•˜๋‹ค).
  2. Objects ์˜ Keys ๋Š” ํ•„์š”ํ•˜์ง€ ์•Š์œผ๋ฉฐ, ๋ชจ๋“  Values ๋ฅผ ๋Œ€์ƒ์œผ๋กœ ๋ฐ˜๋ณต์„ ํ•ด์•ผํ•  ๊ฒฝ์šฐ๋Š” Object.values๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด๋ผ.
  3. Objects ์˜ Keys ๊ฐ€ ํ•„์š”ํ•œ ๋ฐ˜๋ณต๋ฌธ์„ ์‚ฌ์šฉํ•  ๋•Œ๋Š” for-in์€ ๊ฒฐ์ฝ” ๋Š๋ฆฌ์ง€ ์•Š๋‹ค. ํŽธ๋ฆฌํ•˜๋‹ค! ๊ทธ๋Ÿฌ๋ฏ€๋กœ for-in์„ ์จ๋ผ!




Reference

  1. โ€œforโ€ฆin.โ€ MDN Web Docs. Sep. 25, 2023, accessed Apr. 09, 2024, MDN - forโ€ฆin.
  2. โ€œforโ€ฆof.โ€ MDN Web Docs. Aug. 25, 2023, accessed Apr. 09, 2024, MDN - forโ€ฆof.
  3. โ€œIteration protocols.โ€ MDN Web Docs. Dec. 27, 2023, accessed Apr. 09, 2024, MDN - Iteration protocols.