๋ฐ˜์‘ํ˜•

Map์€ key, value๋กœ ์ด๋ฃจ์–ด์ง„ ์ˆœ์„œ๊ฐ€ ์žˆ๋Š” ์ปฌ๋ ‰์…˜์ด๋‹ค. ์‚ฝ์ž… ์ˆœ์„œ๋ฅผ ๊ธฐ์–ตํ•˜๋ฉฐ key/value ์ถ”๊ฐ€/์ œ๊ฑฐ๊ฐ€ ๋นˆ๋ฒˆํ•  ๋•Œ ๊ฐ์ฒด๋ณด๋‹ค ๋” ์ข‹์€ ์„ฑ๋Šฅ์„ ๊ฐ€์ง„๋‹ค MDN - Map ํŽ˜์ด์ง€๋ฅผ ๋ณด๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ์ ํ˜€์žˆ๋‹ค.

Map: ํ‚ค-๊ฐ’ ์Œ์˜ ๋นˆ๋ฒˆํ•œ ์ถ”๊ฐ€ ๋ฐ ์ œ๊ฑฐ์™€ ๊ด€๋ จ๋œ ์ƒํ™ฉ์—์„œ๋Š” ์„ฑ๋Šฅ์ด ์ข€ ๋” ์ข‹์Šต๋‹ˆ๋‹ค.
Object: ํ‚ค-๊ฐ’ ์Œ์˜ ๋นˆ๋ฒˆํ•œ ์ถ”๊ฐ€ ๋ฐ ์ œ๊ฑฐ์— ์ตœ์ ํ™”๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

 

๋ฒค์น˜๋งˆํฌ ์ถœ์ฒ˜ Perf.link

์‹ค์ œ๋กœ ๋žœ๋ค ์ˆซ์ž 10,000๊ฐœ๋กœ ๊ตฌ์„ฑ๋œ ๊ฐ์ฒด๋ฅผ ์ถ”๊ฐ€/์‚ญ์ œํ•  ๋•Œ Map์€ 901 ops/s, ๊ฐ์ฒด๋Š” 183 ops/s๋กœ ์ธก์ •๋œ๋‹ค. ์ฆ‰, 1์ดˆ ๋™์•ˆ Map์€ 901๋ฒˆ์˜ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•˜๊ณ , ๊ฐ์ฒด๋Š” 183๋ฒˆ์˜ ์ž‘์—…์„ ์ฒ˜๋ฆฌํ•œ๋‹ค. Map์ด ์ผ๋ฐ˜ ๊ฐ์ฒด๋ณด๋‹ค ๊ฑฐ์˜ 4~5๋ฐฐ ๊ฐ€๋Ÿ‰ ๋น ๋ฅด๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค. ๋ฌผ๋ก  ์ด ๋ฒค์น˜๋งˆํฌ๋ฅผ 100% ์‹ ๋ขฐํ•  ์ˆ˜ ์—†์ง€๋งŒ key/value ์ถ”๊ฐ€/์ œ๊ฑฐ์— ์žˆ์–ด Map์ด ๊ฐ์ฒด๋ณด๋‹ค ์ตœ์ ํ™”๋œ ๊ฒƒ์€ ์ž๋ช…ํ•ด๋ณด์ธ๋‹ค.

 

๐Ÿ’กops/s๋Š” ์ดˆ๋‹น ์—ฐ์‚ฐ ํšŸ์ˆ˜๋ฅผ ๊ฐ€๋ฆฌํ‚จ๋‹ค.

 

๋‚ด์žฅ ํ‚ค


ํ‚ค๊ฐ€ ์—†๋Š” ๋นˆ ๊ฐ์ฒด๋ผ๋„ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์˜ ๋ชจ๋“  ๊ฐ์ฒด๋Š” Object.prototype์— ์ •์˜๋œ ์†์„ฑ๊ณผ ๋ฉ”์„œ๋“œ๋ฅผ ์ƒ์†๋ฐ›๋Š”๋‹ค. ์ฆ‰, ๋นˆ ๊ฐ์ฒด๋ผ๋„ ํ”„๋กœํ† ํƒ€์ž… ์ฒด์ธ์— ์˜ํ•ด ์•„๋ž˜์™€ ๊ฐ™์€ ๊ฒฐ๊ณผ๋ฅผ ์ถœ๋ ฅํ•œ๋‹ค. ์ด๋Š” ์ž ์žฌ์ ์ธ ๋ฒ„๊ทธ๋กœ ์ด์–ด์งˆ ์ˆ˜ ์žˆ๋‹ค.

const myMap = {};

myMap.valueOf; // [Function: valueOf]
myMap.toString; // [Function: toString]
myMap.hasOwnProperty; // [Function: hasOwnProperty]
myMap.isPrototypeOf; // [Function: isPrototypeOf]
myMap.propertyIsEnumerable; // [Function: propertyIsEnumerable]
myMap.toLocaleString; // [Function: toLocaleString]
myMap.constructor; // [Function: Object]

 

์ฐธ๊ณ ๋กœ ES2022์— ๊ณต๊ฐœํ•œ Object.hasOwn(obj, propKey) ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•˜๋ฉด ์ธ์ž๋กœ ๋„˜๊ธด ํ”„๋กœํผํ‹ฐ๊ฐ€ ๊ฐ์ฒด ์ž์‹ ์ด ์†Œ์œ ํ•œ ๊ฒƒ์ธ์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค. e.g. Object.hasOwn(myMap, valueOf)false

 

Map์„ ์‚ฌ์šฉํ•˜๋ฉด ์ด๋Ÿฌํ•œ ๋ฌธ์ œ์—์„œ ์ž์œ ๋กœ์šธ ์ˆ˜ ์žˆ๋‹ค.

 

๋ฐ˜๋ณต


๋งŒ์•ฝ ์ƒ์„ฑ์ž ํ•จ์ˆ˜์˜ ํ”„๋กœํ† ํƒ€์ž…์„ ์ˆ˜์ •ํ•œ ์ƒํ™ฉ์—์„œ for in์œผ๋กœ ์ˆœํšŒํ•˜๋ฉด ์ž์‹ ์ด ์†Œ์œ ํ•˜์ง€ ์•Š์€ ํ”„๋กœํผํ‹ฐ๋„ ์ถœ๋ ฅํ•œ๋‹ค. ์ด๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด if (Object.hasOwn(...)) ๊ฐ™์€ ์กฐ๊ฑด์„ ์ถ”๊ฐ€ ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ๋ฒˆ๊ฑฐ๋กญ๋‹ค.

// JS ๋‚ด์žฅ ํ”„๋กœํ† ํƒ€์ž…์„ ์ˆ˜์ •ํ•˜๋Š” ๊ฒƒ์€ ๊ถŒ์žฅํ•˜์ง€ ์•Š๋Š”๋‹ค.
// ์•„๋ž˜๋Š” ์˜ˆ์‹œ๋ฅผ ์œ„ํ•œ ์ˆ˜์ •์ธ ์  ์ฐธ๊ณ 
Object.prototype.fullName = 'Obama';
const obj = { name: 'smith', age: 30 };

for (const key in obj) {
  console.log(key); // name, age, fullName
}

 

Map์€ ์ดํ„ฐ๋Ÿฌ๋ธ”์ด๋ฏ€๋กœ for of ๋ฌธ์œผ๋กœ ์ˆœํšŒํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์œ„์™€ ๊ฐ™์€ ๋ฌธ์ œ๋„ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋˜ํ•œ, ๊ตฌ์กฐ ๋ถ„ํ•ด ํ• ๋‹น์„ ์ด์šฉํ•ด key, value๋ฅผ ํ•œ ๋ฒˆ์— ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์–ด ํŽธํ•˜๋‹ค.

const myMap = new Map([
  ['name', 'smith'],
  ['age', 30],
]);

for (const [key, value] of myMap) {
  console.log(`${key}: ${value}`); // name: smith, age: 30
}

Symbol.iterator ๋ฉ”์„œ๋“œ๊ฐ€ ๊ตฌํ˜„๋˜์–ด ์žˆ๋Š” ๊ฐ์ฒด๋ฅผ ์ดํ„ฐ๋Ÿฌ๋ธ”(iterable)์ด๋ผ๊ณ  ํ•œ๋‹ค. ๊ฐ„๋‹จํžˆ ๋งํ•˜๋ฉด ์ดํ„ฐ๋Ÿฌ๋ธ”์€ ๋ฐ˜๋ณต ๊ฐ€๋Šฅํ•œ ๊ฐ์ฒด๋‹ค. ๋ฐฐ์—ด, ๋ฌธ์ž์—ด์€ Symbol.iterator ๋ฉ”์„œ๋“œ๊ฐ€ ์ด๋ฏธ ๊ตฌํ˜„๋˜์–ด ์žˆ๋Š” ๋Œ€ํ‘œ์ ์ธ ๋‚ด์žฅ ์ดํ„ฐ๋Ÿฌ๋ธ”์ด๋‹ค. ์ดํ„ฐ๋Ÿฌ๋ธ”์„ ์‚ฌ์šฉํ•˜๋ฉด ์–ด๋–ค ๊ฐ์ฒด๋“  โžŠfor of ๋ฐ˜๋ณต๋ฌธ โž‹์ „๊ฐœ ๋ฌธ๋ฒ• โžŒ๋ฐฐ์—ด ๊ตฌ์กฐ๋ถ„ํ•ด ํ• ๋‹น์˜ ๋Œ€์ƒ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

 

ํ‚ค ์ˆœ์„œ


Map์€ ์‚ฝ์ž… ์ˆœ์„œ๋ฅผ ์œ ์ง€ํ•˜๋ฏ€๋กœ, ์š”์†Œ๋ฅผ ์ˆœํšŒํ•  ๋•Œ๋„ ํ•ญ์ƒ ์ผ์ •ํ•œ ์ˆœ์„œ๋กœ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ์ด๋Ÿฐ ํŠน์„ฑ ๋•Œ๋ฌธ์— Map์„ ๊ตฌ์กฐ ๋ถ„ํ•ด ํ• ๋‹นํ•˜์—ฌ ์š”์†Œ์— ๋” ์‰ฝ๊ฒŒ ์•ก์„ธ์Šคํ•  ์ˆ˜ ์žˆ๋‹ค.

const user = { name: 'smith', age: 30 };
const map = new Map(Object.entries(user));
const [firstEntry, secondEntry] = map; // ['name', 'smith'], ['age', 30]
const [[firstKey, firstValue]] = map; // firstKey -> 'name', firstValue -> 30

 

๋ณต์‚ฌ


๊ฐ์ฒด๋Š” ์ „๊ฐœ๋ฌธ๋ฒ•์ด๋‚˜ Object.assign ๋ฉ”์„œ๋“œ๋กœ ์‰ฝ๊ฒŒ ๋ณต์‚ฌํ•  ์ˆ˜ ์žˆ๋‹ค.

const user = { name: 'smith', age: 30 };
const cloned1 = { ...user };
const cloned2 = Object.assign({}, user);

 

Map ์ƒ์„ฑ์ž๋Š” [key, value] ํŠœํ”Œ์˜ ์ดํ„ฐ๋Ÿฌ๋ธ”์„ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ ๊ธฐ์กด ๋งต ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑ์ž์— ์ „๋‹ฌํ•˜๋ฉด ๋ณต์‚ฌํ•  ์ˆ˜ ์žˆ๋‹ค.

const userMap = new Map([
  ['name', 'smith'],
  ['age', 30],
]);

const cloned = new Map(userMap); // Map(2) {'name' => 'smith', 'age' => 30}

 

๊นŠ์€ ๋ณต์‚ฌ๋Š” structuredClone ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.

const users = { smith: { age: 30 }, john: { age: 20 } };
// entries -> [['smith', { age: 30 }], ['john', { age: 20 }]]

// ์–•์€ ๋ณต์‚ฌ
const usersMap = new Map(Object.entries(users));
const clonedMap = new Map(usersMap);
usersMap.get('smith') === clonedMap.get('smith'); // true

// ๊นŠ์€ ๋ณต์‚ฌ
const clonedMap = new Map(structuredClone(usersMap));
usersMap.get('smith') === clonedMap.get('smith'); // false

 

ํ—ฌํผ ํ•จ์ˆ˜ โšก๏ธ


Object.fromEntries, Object.entries ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๊ฐ์ฒด โ‡† Map ๋ณ€ํ˜•์„ ์ž์œ ๋กญ๊ฒŒ ํ•  ์ˆ˜ ์žˆ๋‹ค.

const user = { name: 'smith', age: 30 };
const map = new Map(Object.entries(user)); // Map(2) {'name' => 'smith', 'age' => 30}
const userFromMap = Object.fromEntries(map); // {name: 'smith', age: 30}

 

์ƒˆ๋กœ์šด Map ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•  ๋•Œ [[key ,value], [key, value]] ํŠœํ”Œ์„ ์ธ์ž๋กœ ๋„˜๊ฒจ์•ผ ํ•œ๋‹ค. ๊ทธ๋ฆฌ ์ง๊ด€์ ์ด์ง„ ์•Š๋‹ค. Object.entries๋ฅผ ํ™œ์šฉํ•ด ํ—ฌํผ ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด๋‘๋ฉด ๋” ํŽธํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

const makeMap = (obj) => new Map(Object.entries(obj));
const myMap = makeMap({ name: 'smith', age: 30 }); // Map(2) {'name' => 'smith', 'age' => 30}

 

์•„๋ž˜๋Š” ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‚ฌ์šฉํ–ˆ์„ ๋•Œ ์˜ˆ์‹œ. V ์ œ๋„ค๋ฆญ ํƒ€์ž…์„ ์ „๋‹ฌํ•˜์ง€ ์•Š์œผ๋ฉด unknown์„ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ๊ฐ€์ง€๋ฉฐ, key๋Š” string, value๋Š” V ์ œ๋„ค๋ฆญ ํƒ€์ž…์„ ๊ฐ€์ง€๋Š” Map ๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

const makeMap = <V = unknown,>(obj: Record<string, V>) => {
  return new Map<string, V>(Object.entries(obj));
};

 

Map ํ™œ์šฉ โšก๏ธ


์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„  string๊ณผ symbol ํƒ€์ž…๋งŒ ๊ฐ์ฒด์˜ key๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ๋ฐ˜๋ฉด Map์€ ์›์‹œํ˜•์€ ๋ฌผ๋ก  ๊ฐ์ฒด ๊ฐ™์€ ์ฐธ์กฐํ˜•๋„ key๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

myMap.set({}, value);
myMap.set([], value);
myMap.set(document.body, value);
myMap.set(function () {}, value);

 

๐Ÿ’ก ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋Š” ํŠน์ • ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•œ ์ถ”๊ฐ€ ์ •๋ณด๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์˜๋ฏธํ•œ๋‹ค.

 

์ด๋ฅผ ํ™œ์šฉํ•˜๋ฉด ๊ฐ์ฒด๋ฅผ ์ง์ ‘ ์ˆ˜์ •ํ•˜์ง€ ์•Š๊ณ  ๊ฐ์ฒด์™€ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ์—ฐ๊ฒฐํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์ƒํƒœ๋ฅผ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๋‹ค. ์•„๋ž˜ ์˜ˆ์‹œ์—์„  $h3 ์š”์†Œ์— ๋Œ€ํ•œ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ Map ๊ฐ์ฒด์— ์ €์žฅํ•ด๋‘๊ณ , get ๋ฉ”์„œ๋“œ๋กœ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•˜๊ณ  ์žˆ๋‹ค.

const $h3 = document.querySelector('h3');
const metadata = new Map();

metadata.set($h3, { color: 'red' });
metadata.get($h3); // {color: 'red'}

 

์›๋ณธ ๊ฐ์ฒด(toDoList)์— ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š์œผ๋ฉด์„œ ์ž„์‹œ ๋ฐ์ดํ„ฐ(focused ์ƒํƒœ)๋ฅผ ์„ค์ •ํ•  ๋•Œ๋„ ์œ ์šฉํ•˜๋‹ค.

const metadata = new Map();

metadata.set(toDoList, { focused: true });
metadata.get(toDoList); // {focused: true}

 

WeakMap


๐Ÿ’ก WeakMap๊ณผ ์œ ์‚ฌํ•œ WeakSet๋„ ์žˆ๋‹ค. WeakSet ์—ญ์‹œ key์— ๊ฐ์ฒด(์ฐธ์กฐํ˜•)๋งŒ ์ €์žฅํ•  ์ˆ˜ ์žˆ๋‹ค.

 

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰ํ„ฐ๋Š” ๋™์ ์œผ๋กœ ํ• ๋‹น๋œ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๊ด€๋ฆฌํ•œ๋‹ค. ๊ฐ์ฒด๋‚˜ ํ•จ์ˆ˜๋ฅผ ์ƒ์„ฑํ•˜๋ฉด ๋ฉ”๋ชจ๋ฆฌ์— ํ• ๋‹น๋˜๊ณ , ํ•ด๋‹น ๊ฐ์ฒด๊ฐ€ ๋” ์ด์ƒ ์ฐธ์กฐ๋˜์ง€ ์•Š์œผ๋ฉด(์‚ฌ์šฉํ•˜๋Š” ๊ณณ ์—†์Œ; ๋„๋‹ฌ ๋ถˆ๊ฐ€) ๋ฉ”๋ชจ๋ฆฌ์—์„œ ํ•ด์ œํ•˜์—ฌ ์ž์›์„ ํ™•๋ณดํ•œ๋‹ค.

let user = { name: 'smith', age: 30 };
user = null;
// ์ฐธ์กฐ๋ฅผ null๋กœ ๋ฎ์–ด์จ์„œ ๊ฐ์ฒด์— ๋”์ด์ƒ ์ ‘๊ทผํ•  ์ˆ˜ ์—†์Œ
// ๋”ฐ๋ผ์„œ ์œ„ ๊ฐ์ฒด๋Š” ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰ํ„ฐ์— ์˜ํ•ด ๋ฉ”๋ชจ๋ฆฌ์—์„œ ์‚ญ์ œ๋จ

 

โ‘ ๊ฐ์ฒด ํ”„๋กœํผํ‹ฐ, โ‘ก๋ฐฐ์—ด ์š”์†Œ, โ‘ขMap/Set์„ ๊ตฌ์„ฑํ•˜๋Š” ์š”์†Œ๋Š” ์ž์‹ ์ด ์†ํ•œ ์ž๋ฃŒ๊ตฌ์กฐ๊ฐ€ ๋ฉ”๋ชจ๋ฆฌ์— ๋‚จ์•„ ์žˆ์œผ๋ฉด ๋„๋‹ฌํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ’์œผ๋กœ ์ทจ๊ธ‰ํ•ด์„œ ๋ฉ”๋ชจ๋ฆฌ์—์„œ ์‚ญ์ œ๋˜์ง€ ์•Š๋Š”๋‹ค.

 

์•„๋ž˜ ์ฝ”๋“œ์—์„œ smith ๋ณ€์ˆ˜๋ฅผ null๋กœ ๋ฎ์–ด์ผ๋Š”๋ฐ๋„ ๋ถˆ๊ตฌํ•˜๊ณ  users ์—์„  ์—ฌ์ „ํžˆ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค. smith๊ฐ€ ๊ฐ€๋ฆฌ์ผฐ๋˜ ๊ฐ์ฒด๋Š” users ๋ฐฐ์—ด ์š”์†Œ๋กœ ์กด์žฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰ํ„ฐ ๋Œ€์ƒ์ด ๋˜์ง€ ์•Š์€ ๊ฒƒ์ด๋‹ค.

let smith = { name: 'smith' };
const users = [smith];

smith = null;
console.log(users); // [{"name": "smith"}]

 

Map์—์„œ ๊ฐ์ฒด๋ฅผ key/value๋กœ ์‚ฌ์šฉํ–ˆ์„ ๋•Œ๋„ Map์ด ๋ฉ”๋ชจ๋ฆฌ์— ๋‚จ์•„์žˆ๋Š” ํ•œ key ํ˜น์€ value๋กœ ์‚ฌ์šฉํ•œ ๊ฐ์ฒด๋„ ๋ฉ”๋ชจ๋ฆฌ์— ๊ณ„์† ๋‚จ๊ฒŒ ๋œ๋‹ค.

let smith = { name: 'smith' };
const map = new Map();
map.set(smith, '์Šค๋ฏธ์Šค'); // Map(1) {{name: 'smith'} => '์Šค๋ฏธ์Šค'}

smith = null;
for (const key of map.keys()) console.log(key); // {name: 'smith'}

 

๋ฐ˜๋ฉด WeakMap์—์„œ ๊ฐ์ฒด๋ฅผ key๋กœ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ, ํ•ด๋‹น ๊ฐ์ฒด๋ฅผ ์ฐธ์กฐํ•˜๋Š” ๊ณณ์ด ์—†๋‹ค๋ฉด ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰์…˜ ๋Œ€์ƒ์ด ๋ผ์„œ ๋ฉ”๋ชจ๋ฆฌ์™€ WeakMap์—์„œ ์‚ญ์ œ๋œ๋‹ค.

const weakMap = new WeakMap();
let smith = { name: 'smith' };
weakMap.set(smith, '์Šค๋ฏธ์Šค'); // WeakMap {{name: 'smith'} => '์Šค๋ฏธ์Šค'}

smith = null;
console.log(weakMap); // WeakMap {}

 

WeakMap์˜ key๋Š” ๊ฐ์ฒด๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ํŠน์ง•์ด ์žˆ๋‹ค.

const weakMap = new WeakMap();
const smith = { name: 'smith' };

weakMap.set('smith', smith); // Invalid value used as weak map key
weakMap.set(smith, 'smith'); // OK

 

๐Ÿ’ก Map๊ณผ ๋‹ฌ๋ฆฌ WeakMap์€ ์ดํ„ฐ๋Ÿฌ๋ธ”์ด ์•„๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ for of ๋ฐ˜๋ณต๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค.

 

WeakMap์—์„  keys(), values(), entries() ๊ฐ™์€ ์ˆœํšŒ(traversal) ๋ฉ”์„œ๋“œ๋ฅผ ์ง€์›ํ•˜์ง€ ์•Š๋Š”๋‹ค. ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰ํ„ฐ๊ฐ€ ์ž‘๋™ํ•˜๋Š” ์‹œ์ ์€ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์—”์ง„์ด ๊ฒฐ์ •ํ•˜๋ฉฐ, ์ •ํ™•ํ•œ ๋™์ž‘ ์‹œ์ ์„ ์˜ˆ์ธกํ•  ์ˆ˜ ์—†๋‹ค. ์ฆ‰, ๊ฐ์ฒด๋กœ ์กด์žฌํ•˜๋Š” WeakMap ํ‚ค(key)๊ฐ€ ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰ํ„ฐ์— ์˜ํ•ด ์–ธ์ œ ์‚ญ์ œ๋ ์ง€ ๋ชจ๋ฅด๊ธฐ ๋•Œ๋ฌธ์— WeakMap ์š”์†Œ ๊ฐœ์ˆ˜๋ฅผ ํŒŒ์•…ํ•˜๋Š” ๊ฒƒ๋„ ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค. ์ด๋Ÿฌํ•œ ์ด์œ ๋กœ ์š”์†Œ ์ „์ฒด๋ฅผ ๋Œ€์ƒ์œผ๋กœ ํ•˜๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ์ œ๊ณตํ•˜์ง€ ์•Š๋Š” ๊ฒƒ.

 

WeakMap์—์„  ์•„๋ž˜ ๋ฉ”์„œ๋“œ๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

  • weakMap.get(key)
  • weakMap.set(key, value)
  • weakMap.delete(key)
  • weakMap.has(key)

 

์ง๋ ฌํ™” / ์—ญ์ง๋ ฌํ™”


JSON.stringify(value[, replacer[, space]])

 

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๊ฐ์ฒด๋ฅผ JSON์œผ๋กœ ๋ณ€ํ™˜(์ง๋ ฌํ™”)ํ•  ๋•Œ JSON.stringify๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. JSON.stringify ๋‘๋ฒˆ์งธ ์„ ํƒ์  ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ replacer๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ๋Š”๋ฐ, ์ง๋ ฌํ™”์ „ ๊ฐ’์„ ๋ณ€ํ˜•ํ•˜๊ฑฐ๋‚˜ ์›ํ•˜๋Š” ์†์„ฑ๋งŒ ํฌํ•จ์‹œํ‚ค๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•œ๋‹ค. replacer๋Š” ์šฉ๋„์— ๋”ฐ๋ผ ํ•จ์ˆ˜์™€ ๋ฐฐ์—ด 2๊ฐ€์ง€ ๋ฐฉ์‹์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

  • ํ•จ์ˆ˜ : ๋ณ€ํ™˜ ์ „ ๊ฐ’ ๋ณ€ํ˜•. ํ•จ์ˆ˜ ์ธ์ž๋กœ key, value๋ฅผ ๋ฐ›์Œ
  • ๋ฐฐ์—ด : ์ง€์ •ํ•œ ํ”„๋กœํผํ‹ฐ๋งŒ ๊ฒฐ๊ณผ์— ํฌํ•จ

 

replacer ํ†บ์•„๋ณด๊ธฐ

replacer ํ•จ์ˆ˜ ๋ฐ˜ํ™˜๊ฐ’์ด null, undefined ์ด๋ฉด ๊ฒฐ๊ณผ์—์„œ ์ œ์™ธ๋œ๋‹ค. ๋”ฐ๋ผ์„œ ์•„๋ž˜ replacer ํ•จ์ˆ˜๋Š” string ํƒ€์ž…์˜ value๋Š” ๊ฒฐ๊ณผ์— ํฌํ•จํ•˜์ง€ ์•Š๋Š”๋‹ค.

// ํ•จ์ˆ˜ replacer ์˜ˆ์‹œ 1
function replacer(key, value) {
  if (typeof value === 'string') return undefined;
  return value;
}

const foo = {
  foundation: 'Mozilla',
  model: 'box',
  week: 45,
  transport: 'car',
  month: 7,
};

const jsonString = JSON.stringify(foo, replacer); // '{"week":45,"month":7}'

 

replacer ํ•จ์ˆ˜๋Š” ์ฒซ๋ฒˆ์งธ ์ˆœํšŒ์‹œ JSON.stringify์— ๋„˜๊ฒผ๋˜ ๊ฐ์ฒด๋ฅผ value๋กœ ๋ฐ›๋Š”๋‹ค. ์ด๋•Œ ๊ฐ์ฒด ๊ตฌ์กฐ์˜ value๋ฅผ ๋ฆฌํ„ดํ•˜์ง€ ์•Š์œผ๋ฉด ๋”์ด์ƒ ์ˆœํšŒํ•˜์ง€ ์•Š๋Š”๋‹ค. ์ฆ‰, ๊ฐ์ฒด key/value ์Œ์— ๋Œ€ํ•œ ์ˆœํšŒ๋Š” ๋‘๋ฒˆ์งธ๋ถ€ํ„ฐ ์ด๋ค„์ง€๋Š” ์…ˆ์ด๋‹ค. ๊ฐ์ฒด key์˜ value๊ฐ€ ๊ฐ์ฒด ์ผ ๋•Œ๋„ ์žฌ๊ท€์ ์œผ๋กœ ๋™์ž‘ํ•˜์—ฌ ์ˆœํšŒ ๋ฐฉ์‹์€ ๋™์ผํ•˜๋‹ค.

// ํ•จ์ˆ˜ replacer ์˜ˆ์‹œ 2
function replacer(key, value) {
  console.log('key:', key, '|', 'value:', value);
  if (typeof value === 'string') return value + '-modified';
  return value;
}

JSON.stringify({ name: 'smith' }, replacer);
// [์ˆœํšŒ 1] key: '', value: {name: 'smith'} -> JSON.stringify ์ธ์ž๋กœ ๋„˜๊ฒผ๋˜ ๊ฐ์ฒด
// [์ˆœํšŒ 2] key: 'name', value: 'smith' -> ์ฒซ๋ฒˆ์งธ key/value ์ˆœํšŒ
// [๊ฒฐ๊ณผ] '{"name":"smith-modified"}'

 

replacer๊ฐ€ ๋ฐฐ์—ด์ผ ๋• ํฌํ•จํ•˜๊ณ  ์‹ถ์€ key๋ฅผ ๋ฐฐ์—ด ์š”์†Œ๋กœ ์ถ”๊ฐ€ํ•œ๋‹ค.

// ๋ฐฐ์—ด replacer ์˜ˆ์‹œ
const foo = {
  foundation: 'Mozilla',
  model: 'box',
  week: 45,
  transport: 'car',
  month: 7,
};

JSON.stringify(foo, ['weak, month']); // '{"week":45,"month":7}'

 

Map, Set ์ง๋ ฌํ™”

์œ„์—์„œ ์†Œ๊ฐœํ•œ JSON.stringify ๋ฉ”์„œ๋“œ์˜ replacer ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•˜๋ฉด Map/Set๋„ ์‰ฝ๊ฒŒ ์ง๋ ฌํ™”ํ•  ์ˆ˜ ์žˆ๋‹ค.

function replacer(key, value) {
  // Map์„ ์ผ๋ฐ˜ ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜
  if (value instanceof Map) return Object.fromEntries(value);
  // Set์„ ์ผ๋ฐ˜ ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜
  if (value instanceof Set) return Array.from(value);
  return value;
}

const obj = { set: new Set([1, 2]), map: new Map([['smith', 30]]) };
JSON.stringify(obj, replacer); // '{"set":[1,2],"map":{"smith":30}}'

 

Map, Set ์—ญ์ง๋ ฌํ™”

JSON.parse(text[, reviver])

 

์—ญ์ง๋ ฌํ™”๋Š” JSON.parse ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•œ๋‹ค. JSON.parse๋„ ๋‘๋ฒˆ์งธ ์ธ์ž๋กœ reviver ํ•จ์ˆ˜๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ์œผ๋ฉฐ, value ๊ฐ’์„ ๋ณ€ํ˜•ํ•˜๊ฑฐ๋‚˜ ํŠน์ • ์†์„ฑ์„ ์ œ์™ธํ•  ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.

 

์ž‘๋™ ๋ฐฉ์‹์€ JSON.stringify ๋ฉ”์„œ๋“œ์˜ replacer ํ•จ์ˆ˜์™€ ์œ ์‚ฌํ•˜์ง€๋งŒ, reviver ํ•จ์ˆ˜๋Š” ๊ฐ key-value ์Œ์„ ๋จผ์ € ์ˆœํšŒํ•˜๊ณ , ๋งˆ์ง€๋ง‰ ์ˆœํšŒ์—์„œ ์ตœ์ข…์ ์œผ๋กœ ๋ณ€ํ™˜๋œ ๊ฐ์ฒด๊ฐ€ value๋กœ ์ „๋‹ฌ๋œ๋‹ค. 

function reviver(key, value) {
  console.log('key:', key, '|', 'value:', value);
  return value;
}

JSON.parse('{"name":"smith"}', reviver);
// [์ˆœํšŒ1] key: 'name', value: 'smith'
// [์ˆœํšŒ2] key: '', value: {name: 'smith'}
// [๊ฒฐ๊ณผ] {name: 'smith'}

 

reviver ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•˜๋ฉด ์ง๋ ฌํ™”๋œ Map/Set์„ ์—ญ์ง๋ ฌํ™”ํ•  ์ˆ˜ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ ์•„๋ž˜ ์ฝ”๋“œ์—์„  ๋ชจ๋“  ๊ฐ์ฒด๋ฅผ Map์œผ๋กœ ๋ฐ˜ํ™˜ํ•˜๊ณ  ์žˆ๋‹ค. object ํƒ€์ž…์˜ value๋ฅผ Map์œผ๋กœ ๋ณ€ํ™˜์‹œํ‚ค๋Š” ์กฐ๊ฑด ๋•Œ๋ฌธ์— ๊ทธ๋Ÿฐ๊ฒƒ.

function reviver(key, value) {
  console.log('key:', key, '|', 'value:', value);
  if (Array.isArray(value)) {
    return new Set(value);
  }
  if (value && typeof value === 'object') {
    return new Map(Object.entries(value));
  }
  return value;
}

const obj = { set: new Set([1, 2]), map: new Map([['smith', 30]]) };
const jsonString = JSON.stringify(obj, replacer); // '{"set":[1,2],"map":{"smith":30}}'
JSON.parse(jsonString, reviver);
// [์ˆœํšŒ1] key: 0     | value: 1
// [์ˆœํšŒ2] key: 1     | value: 2
// [์ˆœํšŒ3] key: set   | value: [1, 2] -> Set์œผ๋กœ ๋ณ€ํ™˜
// [์ˆœํšŒ4] key: smith | value: 30
// [์ˆœํšŒ5] key: map   | value: {smith: 30} -> Map์œผ๋กœ ๋ณ€ํ™˜
// [์ˆœํšŒ6] key: ''    | value: {set: Set(2), map: Map(1)} -> Map์œผ๋กœ ๋ณ€ํ™˜
// [๊ฒฐ๊ณผ] Map(2) {'set' => Set(2), 'map' => Map(1)}

 

์ผ๋ฐ˜ ๊ฐ์ฒด์™€ Map/Set์„ ๊ตฌ๋ณ„ํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด ์—†์œผ๋ฏ€๋กœ __type ์ž„์˜ ํ”„๋กœํผํ‹ฐ๋ฅผ ๋งŒ๋“ค์–ด์„œ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.

function replacer(key, value) {
  if (value instanceof Map) {
    return { __type: 'Map', value: Object.fromEntries(value) };
  }
  if (value instanceof Set) {
    return { __type: 'Set', value: Array.from(value) };
  }
  return value;
}

function reviver(key, value) {
  if (value?.__type === 'Set') {
    return new Set(value.value);
  }
  if (value?.__type === 'Map') {
    return new Map(Object.entries(value.value));
  }
  return value;
}

const obj = { set: new Set([1, 2]), map: new Map([['smith', 30]]) };
const jsonString = JSON.stringify(obj, replacer); // '{"set":{"__type":"Set","value":[1,2]},"map":{"__type":"Map","value":{"smith":30}}}'
JSON.parse(jsonString, reviver); // {set: Set(2), map: Map(1)}

 

๋ ˆํผ๋Ÿฐ์Šค


 


๊ธ€ ์ˆ˜์ •์‚ฌํ•ญ์€ ๋…ธ์…˜ ํŽ˜์ด์ง€์— ๊ฐ€์žฅ ๋น ๋ฅด๊ฒŒ ๋ฐ˜์˜๋ฉ๋‹ˆ๋‹ค. ๋งํฌ๋ฅผ ์ฐธ๊ณ ํ•ด ์ฃผ์„ธ์š”
๋ฐ˜์‘ํ˜•