자바스크립트 Set/WeakSet과 Map/WeakMap

Tori·2024년 12월 11일
post-thumbnail

JavaScript Info - Map과 Set
JavaScript Info - WeakMap과 WeakSet
mdn Map
mdn Set
Gmarket Tech - Map이란




자바스크립트의 데이터 타입에는 크게 두 가지가 있다.

기본형(primitive type)

  • 문자열(string)
  • 숫자(number)
  • 불리언(boolean)
  • Bigint
  • null
  • undefined
  • Symbol

참조형(reference type)

  • 객체(object)
  • 배열(Array)
  • 함수(Function)
  • 날짜(Date)
  • 정규표현식(RegExp)
  • ES6에서 추가된 Map, WeakMap, Set, WeakSet 등 (객체의 하위 분류에 속함)



이중 ES6에서 추가된 참조형의 Map, WeakMap, Set, WeakSet에 대해서 자세하게 알아보려고 한다.

[학습 목표]

1. Map, Set이 등장하게 된 이유와 특징, 사용방법에 대해서 알아보기

2. WeakMap과 WeakSet이 무엇인지 알아보기




맵(Map)과 셋(Set)

Map과 Set이 자바스크립트에 등장하게 된 배경

자바스크립트에서 객체(Object)와 배열(Array)에 대해서 한줄로 정리하면 다음과 같다.

객체: 키가 있는 컬렉션을 저장함(키와 값을 넣는 객체)

배열: 순서가 있는 컬렉션을 저장함(복수 개의 데이터셋을 순서대로 담음)

현실 세계를 반영하기엔 이 두 자료구조만으론 부족해서 맵(Map)과 셋(Set)이 등장하게 되었다.


객체와 배열은 하나의 변수에 담긴 데이터셋의 개수가 몇 백, 몇 천개로 증가한 데이터의 조회, 검색, 수정을 수용할 수 있는 형태일까?
빠르게 발전하고 있는 클라이언트의 컴퓨팅 성능과 브라우저의 발달로 비효율적인 코드와 자료구조들이 "효율적인 것처럼" 보이기만 할 뿐인 것이 아닐지 살펴봐야 한다.

-> 객체와 배열 형태의 자료를 맵의 형태로 적용해 나갈 수 있을 것인지에 대해 알아보자.



Map

Map 객체는 키와 값의 쌍으로 이루어진 컬렉션이다. Map 객체는 객체와 유사하지만 다음과 같은 차이가 있다.

구분객체Map 객체
키로 사용할 수 있는 값문자열 또는 심볼 값객체를 포함한 모든 값
이터러블XO
요소 개수 확인Object.keys(obj).lengthmap.size



맵의 주요 메서드와 프로퍼티

주요 메서드 및 프로퍼티설명
new Map([iterable])맵 만들기, [key, value] 쌍이 있는 iterable(예: 배열)을 선택적으로 넘길 수 있는데, 이때 넘긴 이터러블 객체는 맵 초기화에 사용된다.
map.set(key, value)key를 이용해 value를 저장
map.get(key)key에 해당하는 값을 반환하고, key가 존재하지 않으면 undefined를 반환
map.has(key)key가 존재하면 true, 존재하지 않으면 flase를 반환
map.delete(key)key에 해당하는 값을 삭제
map.clear()맵 안의 모든 요소를 제거
map.size요소의 개수를 반환
  • 객체(Object)의 값 개수를 구하려면 Object.keys(obj).length 등의 형태로 확인해야 하지만, Map은 계산 절차 없이 map.size 값으로 다른 객체 선언 없이 상시 확인할 수 있다.



객체와 맵에서 키가 어떻게 다른지 확인하기

  • 객체는 키를 문자형으로 변환한다.
  • 맵은 키의 타입을 변환시키지 않고 그대로 유지한다.

예시 코드

let map = new Map();

map.set('1', 'string1'); // 문자열 키
map.set(1,'number1'); // 숫자형 키
map.set(true, 'boolean1'); // 불린형 키

// 아래의 코드는 출력되는 값이 다르다!
console.log(map.get('1')); // 'string1'
console.log(map.get(1)); // number1

console.log(map.size); // 3
console.log(map); /* 
Map(3) {'1' => 'string1', 1 => 'number1', true => 'boolean1'}
  [[Entries]]
    0: {"1" => "string1"}
    1: {1 => "number1"}
    2: {true => "boolean1"}
*/



맵은 객체와 달리 키를 문자형으로 변환하지 않고, 키엔 자료형 제약이 없다.

map[key]는 Map을 쓰는 바른 방법이 아니다.
-> map[key] = 2로 값을 설정하는 것 같이 map[key]를 사용할 수 있긴하다. 하지만 이 방법은 map을 일반 객체처럼 취급하게 된다.
따라서 여러 제약이 생기게 된다.

map을 사용할 땐 map 전용 메서드 set, get 등을 사용해야만 한다!



맵은 키로 객체를 허용한다.

예시 코드

let hoho = { name: "Hoho" };

// 고객의 가게 방문 횟수를 세본다고 가정
let visitsCountMap = new Map();

// Hoho를 맵의 키로 사용
visitsCountMap.set(hoho, 100);

console.log(visitsCountMap.get(hoho)); // 100
  • 객체의 키는 문자열 또는 심볼만 이용할 수 있는 반면, 맵에서는 문자열, 숫자, 불리언, 심볼 등의 다양한 자료형을 키값으로 이용할 수 있다. 또한 배열, 객체, 맵, weakMap 등의 모든 복잡한 형태로 키 값으로서 이용할 수 있다.



객체를 키로 사용할 수 있다는 점은 의 가장 중요한 기능 중 하나이다.
객체에는 문자열 키를 사용할 수 있다. 하지만, 객체 키는 사용할 수 없다.


객체형 키를 객체에 써보는 예시 코드를 통해 살펴보자.

let hoho = { name: "Hoho" };

let visitsCountObj = {}; // 객체를 하나 만듦

visitsCountObj[hoho] = 100; // 객체(hoho)를 키로해서 객체에 값(100)을 저장해보기

// 원하는 값(100)을 얻으려면 아래와 같이 키가 들어갈 자리에 "object Object"를 써야한다.
console.log(visitsCountObj["[object Object]"]); // 100

visitsCountObj는 객체이기 때문에 모든 키를 문자형으로 변환시킨다.
이 과정에서 hoho는 문자형으로 변환되어 "[object Object]"가 된다.



맵이 키를 비교하는 방식

맵은 SameValueZero라 불리는 알고리즘을 사용해 값의 등가 여부를 확인한다. 이 알고리즘은 일치 연산자 ===와 거의 유사하지만, NaNNaN을 같다고 취급하는 것에서 일치 연산자와 차이가 있다.
따라서 맵에선 NaN도 키로 쓸 수 있다. (이 알고리즘은 수정하거나 커스터마이징이 불가)

  • 일치 비교 연산자 ===을 사용하면 NaN과 NaN을 다르다고 평가한다. 하지만 Map 객체는 NaN과 NaN을 같다고 평가하여 중복 추가를 허용하지 않는다.
  • +0과 -0은 일치 비교 연산자 ===와 마찬가지로 같다고 평가하여 중복 추가를 허용하지 않는다.

예시 코드

console.log(NaN === NaN); // false
console.log(+0 === -0); // true

const map = new Map();

// NaN과 NaN을 같다고 평가하여 중복 추가를 허용하지 않음
map.set(NaN, 'value1').set(NaN, 'value2');
console.log(map); // { NaN => 'value2' }

// +0과 -0을 같다고 평가하여 중복 추가를 허용하지 않음
map.set(+0, 'value1').set(-0, 'value2');
console.log(map); // { NaN => 'value2', 0 => 'value2' }



map.set() 체이닝(chaining)

  • map.set을 호출할 때 마다 맵 자신이 반환된다.
  • set 메서드는 새로운 요소가 추가된 Map 객체를 반환한다. 따라서 set 메서드를 호출한 후에 set 메서드를 연속적으로 호출(method chaining) 할 수 있다.
map.set('1', 'string1').set(1, 'number1').set(true, 'boolean1');



map의 key에 중복값이 있으면 마지막 값으로 적용된다.

이점은 객체도 마찬가지이다.

let map1 = new Map([['a', 100], ['b', 200], ['a', 300]]);

console.log(map1); // Map(2) {'a' => 300, 'b' => 200}



map의 요소에 반복 작업하기

아래의 세 가지 메서드를 사용하면 맵의 각 요소에 반복 작업을 할 수 있다.

- map.keys()

: 각 요소의 키를 모은 반복 가능한(iterable, 이터러블) 객체를 반환, 모든 키의 배열

- map.values()

: 각 요소의 값을 모은 이터러블 객체를 반환, 모든 값의 배열

- map.entries()

: 요소의 [키, 값] 을 한 쌍으로 하는 이터러블 객체를 반환한다. [키, 값] 쌍의 배열,
이 이터러블 객체는 for ... of 반복문의 기초로 쓰임.


예시 코드

let recipeMap = new Map([
  ['cucumber', 500], 
  ['tomatoes', 350],
  ['onion',    50]
]);

// 키(vegetable)를 대상으로 순회
for (let vegetable of recipeMap.keys()) {
  console.log(vegetable); // cucumber, tomatoes, onion
}

// 값(amount)을 대상으로 순회
for (let amount of recipeMap.values()) {
  console.log(amount); // 500, 350, 50
}

// [키, 값] 쌍을 대상으로 순회 -> recipeMap.entries()와 동일
for (let entry of recipeMap) {
  console.log(entry); // ['cucumber', 500], ['tomatoes', 350], ['onion', 50]
}

// [키, 값] 쌍을 대상으로 순회 -> [key, value]로 배열 구조 분해 할당하여 entry를 명시적으로 사용
for (let [key, value] of recipeMap) {
  console.log([key, value] ); // ['cucumber', 500], ['tomatoes', 350], ['onion', 50]
}

// entries()를 명시적으로 사용
for (let [key, value] of recipeMap.entries()) {
  console.log([key, value] );
}

맵은 삽입 순서를 기억한다.

맵은 값이 삽입된 순서대로 순회를 실시한다. 객체가 프로퍼티 순서를 기억하지 못하는 것과는 다르다.



맵은 배열과 유사하게 내장 메서드 forEach를 지원한다.

let recipeMap = new Map([
  ['cucumber', 500], 
  ['tomatoes', 350],
  ['onion',    50]
]);

// 각 (키, 값) 쌍을 대상으로 함수를 실행
recipeMap.forEach((value, key, map) => {
  console.log(`${key}: ${value}`); // cucumber: 500, tomatoes: 350, onion: 50
})



Object.entries - 객체를 맵으로 바꾸기

각 요소가 키-값 쌍인 배열이나 이터러블 객체를 초기화 용도로 맵에 전달해 새로운 맵을 만들 수 있다.

예시 코드

// 각 요소가 [키, 값] 쌍인 배열
let map = new Map([['1', 'string1'], [1, 'number1'], [true, 'boolean1']]);

console.log(map.get('1')); // string1

  • 평범한 객체를 이용해서 맵을 만들고 싶으면 내장 메서드 Object.entries(obj)를 활용하면 된다.
  • 이 메서드는 객체의 키-값 쌍을 요소([key, value])를 가지는 배열을 반환한다.

예시 코드

let obj = {
  name: "Hoho",
  age: 10,
};

let map = new Map(Object.entries(obj));

console.log(map); // Map(2) {'name' => 'Hoho', 'age' => 10}
console.log(map.get('name')); // Hoho

Object.entries를 사용해 객체 obj를 배열 [ ["name","Hoho"], ["age", 10] ]로 바꾸고, 이 배열을 이용해 새로운 맵을 만들어보았다.



Object.fromEntries - 맵을 객체로 바꾸기

Object.entires(obj)를 사용해서 평범한 객체를 map으로 바꾸는 방법을 위에서 알아보았는데 이번에는 Object.fromEntries를 사용해서 맵을 객체로 바꿔보려고 한다.
이 메서드는 각 요소가 [키, 값] 쌍인 배열을 객체로 바꿔준다.

예시 코드

let prices = Object.fromEntries([
  ['banana', 1],
  ['orange', 2],
  ['meat', 4]
]);

console.log(prices); // {banana: 1, orange: 2, meat: 4}
console.log(prices.meat); // 4



Object.fromEntries를 사용해 맵을 객체로 바꾸는 걸 언제 사용할까?

자료가 맵에 저장되어있는데, 서드파티 코드에서 자료를 객체형태로 넘겨받길 원할 때 이 방법을 사용할 수 있다.

예시 코드

let map = new Map();
map.set('banana', 1);
map.set('orange', 2);
map.set('meat', 4);

let obj = Object.fromEntries(map.entries()); // 맵을 일반 객체로 변환(*)

// map이 객체가 되었음
console.log(obj); // {banana: 1, orange: 2, meat: 4}
console.log(obj.banana); // 1

map.entries()를 호출하면 맵의 [키, 값]을 요소로 가지는 이터러블을 반환한다.
Object.fromEntries를 사용하기 위해 딱 맞는 형태이다.


let obj = Object.fromEntries(map.entries()); 이 코드를 더 짧게 줄이는 것도 가능하다.

.entries() 생략 할 수 있음

let obj = Object.fromEntries(map);
  • Object.fromEntries는 인수로 이터러블 객체를 받기 때문에 짧게 줄인 코드도 이전 코드와 동일하게 동작한다.
  • 꼭 배열을 전달해줄 필요는 없다.
  • map에서 일반적인 반복은 map.entries()를 사용했을 때와 같은 키-값 쌍을 반환한다. 따라서 map과 동일한 키-값을 가진 일반 객체를 얻게 된다.



맵을 사용해야하는 가장 주요한 이유

맵(Map)을 사용해야 하는 가장 주요한 이유는 성능이다. 빈번히 key-value 쌍을 추가하고 삭제할 때 객체(Object)보다 더 빠른 속도를 보인다.

대다수의 단순한 프론트엔드 웹에서는 큰 차이를 보이지 않을 수 있지만, 수 백개, 수 천개의 데이터가 동시다발적으로 생성, 삭제가 이뤄져야 하는 자료형에서는 가시적인 차이를 확인할 수있다.



맵(Map)을 객체(Object)보다 선호해야 할 순간은?

맵(Map) 객체는 키 컬렉션이 있는 객체의 한 종류로서, 객체의 속성(Property)를 자주 변경해야 할 때 그 빛을 발한다. 자주 순회가 일어날 때에도 반복자(Iterator)를 통해서 깔끔하게 순회할 때에 장점이 있는데 모든 객체(Object)를 맵(Map)으로 변환하는 것이 유리하지는 않다.

작은 컬렉션을 가지고 있는 경우, 또는 빠른 프로토타이핑을 원하는 임의의 개발 팀에서 맵(Map)을 적용하는 것은 다소 생산성을 떨어트릴 수있는 요인이 되기도 할 수 있다.

최적의 성능을 요구하는 서비스에 적용하는 것을 추천한다.







Set

Set 객체는 중복되지 않는 유일한 값들의 집합(Set)이다. Set 객체는 배열과 유사하지만 다음과 같은 차이가 있다.

구분배열Set 객체
동일한 값을 중복하여 포함할 수 있다.OX
요소 순서에 의미가 있다.OX
인덱스로 요소에 접근할 수 있다.OX
  • 셋은 중복을 허용하지 않는 값을 모아놓은 특별한 컬렉션이다.
  • 셋에 키가 없는 값이 저장된다.



셋의 주요 메서드와 프로퍼티

주요 메서드 및 프로퍼티설명
new Set([iterable])셋을 만듦. 이터러블 객체를 선택적으로 전달받을 수 있는데(대개 배열을 전달받음), 이터러블 객체 안의 요소는 셋을 초기화하는데 쓰인다.
set.add(value)값을 추가하고 셋 자신을 반환, 셋 내에 이미 value가 있는 경우 아무런 작업을 하지 않음
set.delete(value)값을 제거. 호출 시점에 셋 내에 값이 있어서 제거에 성공하면 true, 아니면 false를 반환
set.has(value)셋 내에 값이 존재하면 true, 아니면 flase를 반환
set.clear()셋을 비움
set.size셋에 몇 개의 값이 있는지 세줌



셋 내의 값에 중복이 없는 이유

셋 내에 동일한 값(value)이 있다면 set.add(value)를 아무리 많이 호출해도 아무런 반응이 없을 것이다. 셋 내의 값에 중복이 없는 이유가 바로 이 때문이다.

방문자 방명록을 만드는 상황을 가정해보자. 한 방문자가 여러 번 방문해도 방문자를 중복해서 기록하지 않겠다고 결정 내린 상황, 즉 한 방문자는 '단 한 번만 기록'되어야 한다.

이때 적합한 자료구조가 Set이다.

예시 코드

let set = new Set();

let hoho = { name: "Hoho" };
let koko = { name: "Koko" };
let mary = { name: "Mary" };

// 어떤 고객(hoho, mary)는 여러 번 방문할 수 있다.
set.add(hoho);
set.add(koko);
set.add(mary);
set.add(hoho);
set.add(mary);

// 셋에는 유일무이한 값만 저장된다.
console.log(set.size); // 3

for (let user of set) {
  console.log(user.name); // Hoho, Koko, Mary 순으로 출력
}

대신 배열을 사용하여 방문자 정보를 저장한 후, 중복 값 여부는 배열 메서드인 arr.find를 이용해 확인할 수도 있다.
하지만, arr.find는 배열 내 요소 전체를 뒤져 중복 값을 찾기때문에 보다 성능 면에서 떨어진다.
반면 은 값의 유일무이함을 확인하는 데 최적화되어있다.



셋의 값에 반복 작업하기

for ... offorEach를 사용하면 셋의 값을 대상으로 반복 작업을 수행할 수 있다.

예시 코드

let set = new Set(["oranges", "apples", "bananas"]);

for (let value of set) {
  console.log(value); // oranges apples bananas
}

// forEach를 사용해도 동일하게 동작한다.
set.forEach((value, valueAgain, set) => {
  console.log(value); // oranges apples bananas
});

한 가지 흥미로운 점 - 동일한 값이 인수에 두 번 등장

forEach에 쓰인 콜백 함수는 세 개의 인수를 받는데,
첫 번째는 , 두 번째도 같은 값인 valueAgain을 받고있다.
세 번째는 목표하는 객체(셋)이다.

동일한 값이 인수에 두 번 등장하고 있다.
-> 이렇게 구현된 이유는 맵과의 호환성 때문이다.

맵의 forEach에 쓰인 콜백이 세 개의 인수를 받을 때를 위해서이다.
-> 이렇게 구현을 해놓았기 때문에 맵을 셋으로 혹은 셋을 맵으로 교체하기가 쉬워진다.



set의 요소에 반복 작업하기

셋에도 맵과 마찬가지로 반복 작업을 위한 메서드가 있다.

  • set.keys()
    : 셋 내의 모든 값을 포함하는 이터러블 객체를 반환

  • set.values()
    : set.keys와 동일한 작업을 한다. 맵과의 호환성을 위해 만들어진 메서드다.

  • set.entries()
    : 셋 내의 각 값을 이용해 만든 [value, value] 배열을 포함하는 이터러블 객체를 반환한다. 맵과의 호환성을 위해 만들어졌다.



Map과 Set 정리

  • 맵과 셋에 반복 작업을 할 땐, 해당 컬렉션에 요소나 값을 추가한 순서대로 반복 작업이 수행된다.
  • 따라서 이 두 컬렉션은 정렬이 되어있지 않다고 할 수 없다.
  • 그렇지만 컬렉션 내 요소나 값을 재정렬하거나(배열에서 인덱스를 이용해 요소를 가져오는 것처럼) 숫자를 이용해 특정 요소나 값을 가지고 오는 것은 불가능하다.







WeakMap과 WeakSet

가비지 컬렉션에서 자바스크립트 엔진은 도달 가능한 (그리고 추후 사용될 가능성이 있는) 값을 메모리에 유지한다.

예시 코드

let hoho = { name: "Hoho" };

// 위 객체는 hoho라는 참조를 통해 접근할 수 있다.

// 그런데 참조를 null로 덮어쓰면 위 객체에 더이상 도달이 가능하지 않게 되어 객체가 메모리에서 삭제된다.
hoho = null;

자료구조를 구성하는 요소도 자신이 속한 자료구조가 메모리에 남아있는 동안 대게 도달 가능한 값으로 취급되어 메모리에서 삭제되지 않는다.
객체의 프로퍼티나 배열의 요소, 맵이나 셋을 구성하는 요소들이 이에 해당된다.


배열에 객체 하나 추가해보는 예시 코드

이때 배열이 메모리에 남아있는 한 배열의 요소인 이 객체도 메모리에 남아있게 된다.
이 객체를 참조하는 것이 아무것도 없더라도 말이다.

let hoho = { name: "Hoho" };
let arr = [ hoho ];

hoho = null;

// hoho를 나타내는 객체는 배열의 요소이기 때문에 가바지 컬렉터의 대상이 되지 않음
// arr[0]을 이용하면 해당 객체를 얻는 것도 가능
console.log(arr[0]); // {name: 'Hoho'}

Map에서 객체를 키로 사용한 경우 역시, 맵이 메모리에 있는 한 객체도 메모리에 남아 가비지 컬렉터의 대상이 되지 않음

예시 코드

let hoho = { name: "Hoho" };

let map = new Map();
map.set(hoho, ".....");

hoho = null; // 참조를 null로 덮어씀

// hoho를 나타내는 객체는 맵 안에 저장되어있다.
// map.keys()를 이용하면 해당 객체를 얻는 것도 가능하다.
for (let obj of map.keys()) {
  console.log(obj);
}

console.log(map.size);

이런 관점에서 WeakMap은 일반 Map과 전혀 다른 양상을 보인다.
위크맵을 사용하면 키로 쓰인 객체가 가비지 컬렉션의 대상이 된다.





WeakMap

위크맵의 차이

1. 위크맵의 키가 반드시 객체여야한다.

  • 원시값은 위크맵의 키가 될 수 없다.

예시 코드

let weakMap = new WeakMap();
let obj = {};

weakMap.set(obj, "ok"); // 정상적으로 동작 (객체 키)

// 문자열("test")은 키로 사용할 수 없다.
weakMap.set("test", "error!"); // Uncaught TypeError: Invalid value used as weak map key

위크맵의 키로 사용된 객체를 참조하는 것이 아무것도 없다면 해당 객체는 메모리와 위크맵에서 자동으로 삭제된다.

예시 코드

let hoho = { name: "Hoho" };

let weakMap = new WeakMap();
weakMap.set(hoho, "....");

hoho = null; // 참조를 덮어씀

// hoho를 나타내는 객체는 이제 메모리에서 지워진다.
console.log(weakMap); 
// WeakMap {}
//   [[Entries]]
//     No properties
  • hoho를 나타내는 객체는 오로지 위크맵의 키로만 사용되고 있으므로, 참조를 덮어쓰게되면 이 객체는 위크맵과 메모리에서 자동으로 삭제된다.



2. 위크맵은 반복 작업과 key(), values(), entiries() 메서드를 지원하지 않는다.

따라서 위크맵에선 키나 값 전체를 얻는 게 불가능하다.

위크맵이 지원하는 메서드

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

왜 이렇게 적은 메서드만 제공할까?

원인은 가비지 컬렉션의 동작 방식 때문이다.

위 예시의 hoho를 나타내는 객체처럼 객체는 모든 참조를 잃게 되면 자동으로 가비지 컬렉션의 대상이 된다.
그런데 가비지 컬렉션의 동작 시점은 정확히 알 수 없다.

가비지 컬렉션이 일어나는 시점은 자바스크립트 엔진이 결정한다.
객체는 모든 참조를 잃었을 때, 그 즉시 메모리에서 삭제될 수도 있고, 다른 삭제 작업이 있을 때 까지 대기하다가 함께 삭제될 수도 있다.

현재 위크맵에 요소가 몇 개 있는 지 정확히 파악하는 것 자체가 불가능하다.

가비지 컬렉터가 한 번에 메모리를 청소할 수도 있고, 부분 부분 메모리를 청소할 수도 있으므로 위크맵의 요소(키/값) 전체를 대상으로 무언가를 하는 메서드는 동작 자체가 불가능하다.



위크맵을 어떤 경우에 사용할 수 있을까?

1. 추가 데이터

위크맵은 부차적인 데이터를 저장할 곳이 필요할 때 그 진가를 발휘한다.

서드파티 라이브러리와 같은 외부 코드에 속한 객체를 가지고 어떤 작업을 해야할 때 이 객체에 데이터를 추가해줘야 하는데, 추가할 데이터는 객체가 살아있는 동안에만 유효한 상황이라 가정할 때 위크맵을 사용할 수 있다.

위크맵에 원하는 데이터를 저장하고, 이때 키는 객체를 사용하면 된다.
이렇게 하면 객체가 가비지 컬렉션의 대상이 될 때, 데이터도 함께 사라진다.


2. 캐싱

위크맵은 캐싱(caching)이 필요할 때 유용하다.
캐싱은 시간이 오래 걸리는 작업의 결과를 저장해서 연산 시간과 비용을 절약해주는 기법이다. 동일한 함수를 여러 번 호출해야 할 때, 최초 호출 시 반환된 값을 어딘가에 저장해 놓았다가 그 다음엔 함수를 호출하는 대신 저장된 값을 사용하는 게 캐싱의 실제 예다.







WeakSet

  • WeakSet은 Set과 유사한데 객체만 저장할 수 있다는 점이 다르다. 원시값은 저장할 수 없다.
  • 셋 안의 객체는 도달 가능할 때만 메모리에서 유지된다.
  • 셋과 마찬가지로 위크셋이 지원하는 메서드는 몇가지 없다.
    - add, has, delete를 사용할 수 있고,
    - size, keys()나 반복 작업 관련 메서드는 사용할 수 없다.

weakMap과 유사하게 weakSet도 부차적인 데이터를 저장할 때 사용할 수 있다. 다만, 위크셋엔 위크맵처럼 복잡한 데이터를 저장하지 않는다.

대신 truefalse 같은 간단한 답변을 얻는 용도로 사용된다.
위크셋에 저장되는 값들은 객체이다.



WeakMap과 WeakSet의 단점

반복 작업이 불가능하다. 위크맵이나 위크셋에 저장된 자료를 한 번에 얻는 게 불가능하다.

이런 단점은 불편함을 초래하는 것 같아 보이지만, 위크맵과 위크셋을 이용해 할 수 있는 주요 작업을 방해하진 않는다.

위크맵과 위크셋은 객체와 함께 '추가' 데이터를 저장하는 용도로 쓸 수 있다.



WeakMap과 WeakSet요약

위크맵

  • 위크맵은 맵과 유사한 컬렉션이다.
  • 위크맵을 구성하는 요소의 키는 오직 객체만 가능하다.
  • 키로 사용된 객체가 메모리에서 삭제되면 이에 대응하는 값 역시 삭제된다.

위크셋

  • 위크셋은 셋과 유사한 컬렉션이다.
  • 위크셋엔 객체만 저장할 수 있다.
  • 위크셋에 저장된 객체가 도달 불가능한 상태가 되면 해당 객체는 메모리에서 삭제된다.

두 자료구조 모두 구성 요소 전체를 대상으로 하는 메서드를 지원하지 않는다. 구성 요소 하나를 대상으로 하는 메서드만 지원한다.



WeakMap과 WeakSet을 언제 활용할까

객체엔 ‘주요’ 자료를, 위크맵과 위크셋엔 ‘부수적인’ 자료를 저장하는 형태로 위크맵과 위크셋을 활용할 수 있다.
객체가 메모리에서 삭제되면, (그리고 오로지 위크맵과 위크셋의 키만 해당 객체를 참조하고 있다면) 위크맵이나 위크셋에 저장된 연관 자료들 역시 메모리에서 자동으로 삭제된다.

profile
🌿

0개의 댓글