위크맵
위크맵의 키는 반드시 객체여야한다. 원시값은 위크맵의 키가 될 수 없다.
let weakMap = new WeakMap();
let obj = {};
weakMap.set(obj, "ok"); //정상적으로 동작합니다(객체 키).
// 문자열("test")은 키로 사용할 수 없습니다.
weakMap.set("test", "Whoops"); // Error: Invalid value used as weak map key
let john = { name: "John" };
let weakMap = new WeakMap();
weakMap.set(john, "...");
john = null; // 참조를 덮어씀
// john을 나타내는 객체는 이제 메모리에서 지워집니다!
- 키로 사용된 객체가 참조하는것이 아무것도 없다면 해당 객체는 메모리와 위크맵에서 자동으로 삭제된다.
- john을 나타내는 객체는 오로지 위크맵의 키로만 사용되고 있으므로, 참조를 덮어쓰게 되면
이 객체는 위크맵과 메모리에서 자동으로 삭제된다.
위크맵이 지원하는 메서드
weakMap.get(key)
weakMap.set(key, value)
weakMap.delete(key)
weakMap.has(key)
유스 케이스: 추가데이터
위크맵은 부차적인 데이터를 저장할 곳이 필요할 때 그 진가를 발휘한다.
서드파티 라이브러리와 같은 외부 코드에 '속한' 객체를 가지고 작업을 해야한다고 할때, 이 객체에
데이터를 추가해줘야 하는데 추가해 줄 데이터는 객체가 살아있는 동안에만 유효한 상황일때 위크맵을 사용할 수 있다.
위크맵에 원하는 데이터를 저장하고 객체가 가비지 컬렉션의 대상이 될 때, 데이터도 함께 사라진다.
weakMap.set(john, "비밀문서");
// john이 사망하면, 비밀문서는 자동으로 파기됩니다.
예시)
아래에 사용자의 방문 횟수를 세어 주는 코드가 있습니다. 관련 정보는 맵에 저장하고 있는데 맵 요소의 키엔 특정 사용자를 나타내는 객체를, 값엔 해당 사용자의 방문 횟수를 저장하고 있습니다. 어떤 사용자의 정보를 저장할 필요가 없어지면(가비지 컬렉션의 대상이 되면) 해당 사용자의 방문 횟수도 저장할 필요가 없어질 겁니다.
아래 함수는 맵을 사용해 사용자의 방문 횟수를 세줍니다.
// 📁 visitsCount.js
let visitsCountMap = new Map(); // 맵에 사용자의 방문 횟수를 저장함
// 사용자가 방문하면 방문 횟수를 늘려줍니다.
function countUser(user) {
let count = visitsCountMap.get(user) || 0;
visitsCountMap.set(user, count + 1);
}
// 📁 main.js
let john = { name: "John" };
countUser(john); // John의 방문 횟수를 증가시킵니다.
// John의 방문 횟수를 셀 필요가 없어지면 아래와 같이 john을 null로 덮어씁니다.
john = null;
이제 john을 나타내는 객체는 가비지 컬렉션의 대상이 되어야 하는데, visitsCountMap의 키로 사용되고 있어서
메모리에서 삭제되지 않습니다.
특정 사용자를 나타내는 객체가 메모리에서 사라지면 해당 객체에 대한 정보(방문횟수)도 손수 지워줘야하는 상황입니다.
이렇게 하지 않으면 visitsCountMap가 차지하는 메모리 공간이 한없이 커지고, 애플리케이션 구조가 복잡할때는
이렇게 쓸모 없는 데이터를 수동으로 비워주는게 꽤 골치아픕니다.
이런 문제는 위크맵을 사용해 예방할 수 있습니다.
// 📁 visitsCount.js
let visitsCountMap = new WeakMap(); // 위크맵에 사용자의 방문 횟수를 저장함
// 사용자가 방문하면 방문 횟수를 늘려줍니다.
function countUser(user) {
let count = visitsCountMap.get(user) || 0;
visitsCountMap.set(user, count + 1);
}
유스 케이스: 캐싱
위크맵은 캐싱이 필요할 때 유용합니다. 캐싱은 시간이 오래걸리는 작업의 결과를 저장해서 연산 시간과 비용을 절약해주는 기법입니다. 동일한 함수를 여러 번 호출해야 할 때, 최초 호출 시 반환된 값을 어딘가에 저장해 놓았다가 그다음엔 함수를 호출하는 대신 저장된 값을 사용하는게 캐싱의 실례입니다.
// 📁 cache.js
let cache = new Map();
// 연산을 수행하고 그 결과를 맵에 저장합니다.
function process(obj) {
if (!cache.has(obj)) {
let result = /* 연산 수행 */ obj;
cache.set(obj, result);
}
return cache.get(obj);
}
// 함수 process()를 호출해봅시다.
// 📁 main.js
let obj = {/* ... 객체 ... */};
let result1 = process(obj); // 함수를 호출합니다.
// 동일한 함수를 두 번째 호출할 땐,
let result2 = process(obj); // 연산을 수행할 필요 없이 맵에 저장된 결과를 가져오면 됩니다.
// 객체가 쓸모없어지면 아래와 같이 null로 덮어씁니다.
obj = null;
alert(cache.size); // 1 (엇! 그런데 객체가 여전히 cache에 남아있네요. 메모리가 낭비되고 있습니다.)
// 📁 cache.js
let cache = new WeakMap();
// 연산을 수행하고 그 결과를 위크맵에 저장합니다.
function process(obj) {
if (!cache.has(obj)) {
let result = /* 연산 수행 */ obj;
cache.set(obj, result);
}
return cache.get(obj);
}
// 📁 main.js
let obj = {/* ... 객체 ... */};
let result1 = process(obj);
let result2 = process(obj);
// 객체가 쓸모없어지면 아래와 같이 null로 덮어씁니다.
obj = null;
// 이 예시에선 맵을 사용한 예시처럼 cache.size를 사용할 수 없습니다.
// 하지만 obj가 가비지 컬렉션의 대상이 되므로, 캐싱된 데이터 역시 메모리에서 삭제될 겁니다.
// 삭제가 진행되면 cache엔 그 어떤 요소도 남아있지 않을겁니다.
- 위와 마찬가지로 cache에 Map() 대신 위크맵을 사용하면 수동으로 청소를 하지 않아두 된다.
위크셋(WeakSet)
위크셋은 셋과 유사한데, 객체만 저장할 수 있다는 점이 다르다. (원시값 저장불가능)
위크셋은 위크맵처럼 복잡한 데이터를 저장하지 않는다. 대신 "예"나 "아니오"같은 간단한 답변을 얻는 용도로 사용된다.
let visitedSet = new WeakSet();
let john = { name: "John" };
let pete = { name: "Pete" };
let mary = { name: "Mary" };
visitedSet.add(john); // John이 사이트를 방문합니다.
visitedSet.add(pete); // 이어서 Pete가 사이트를 방문합니다.
visitedSet.add(john); // 이어서 John이 다시 사이트를 방문합니다.
// visitedSet엔 두 명의 사용자가 저장될 겁니다.
// John의 방문 여부를 확인해보겠습니다.
alert(visitedSet.has(john)); // true
// Mary의 방문 여부를 확인해보겠습니다.
alert(visitedSet.has(mary)); // false
john = null;
// visitedSet에서 john을 나타내는 객체가 자동으로 삭제됩니다.
⭐ 요약
위크맵은 맵과 유사한 컬렉션이다. 위크맵을 구성하는 요소의 키는 오직 객체만 가능하다.
키로 사용된 객체가 메모리에서 삭제되면 이에 대응하는 값 역시 삭제된다.
워크셋은 셋과 유사한 컬렉션이다. 위크셋엔 객체만 저장할 수 있다.
위크셋에 저장된 객체가 도달 불가능한 상태가 되면 해당 객체는 메모리에서 삭제된다.
두 자료구조 모두 구성 요소 전체를 대상으로 하는 메서드를 지원하지 않는다.
객체엔 '주요' 자료를, 위크맵과 위크셋엔 '부수적인'자료를 저장하는 형태로 활용할수 있다.
객체가 메모리에서 삭제되면, (그리고 위크맵과 위크셋의 키만 해당 객체를 참조하고 있다면)
위크맵과 위크셋에 저장된 연관 자료들 역시 메모리에서 자동으로 삭제된다.