key가 있는 데이터를 저장한다는 점에서 객체와 유사하지만 key에 다양한 자료형을 허용한다는 점에서 객체와 차이점이 있다
new Map()
: 맵을 만든다map.set(key, value)
: key를 이용해 value를 저장map.get(key)
: key에 해당하는 값을 반환. key가 없으면undefined
반환map.has(key)
: key가 존재하면 true, 존재하지 않으면 false 반환map.delete(key)
: key에 해당하는 값 삭제map.clear()
: 맵 안의 모든 요소 제거map.size
: 요소의 개수 반환 let map = new Map();
map.set('1', 'str1'); // 문자형 key
map.set(1, 'num1'); // 숫자형 key
map.set(true, 'bool1'); // 불린형 key
// 객체는 key를 문자형으로 변환한다
// Mapdms key의 타입을 변환시키지 않고 그대로 유지한다
➕ map[key]
는 Map을 쓰는 바른 방법이 아니다
map[key] = 2
로 값을 설정하는 것 처럼map[key]
를 사용하는 방법은 map을 일반 객체처럼 취급하게 되므로 여러 제약이 생긴다 👉 map 사용 시에는 전용 메서드인 set, get을 사용한다
let jinju = {name: 'Jinju'};
// 고객의 방문 횟수를 세보기
let visitsCountMap = new Map();
// jinju를 map의 key로 사용
visitsCountMap.set(jinju, 123);
alert(visitsCountMap.get(jinju)); // 123
Map은 SameValueZero 알고리즘을 사용해서 값의 등가 여부를 확인한다.
SameValueZero 알고리즘은 일치 연산자 ===
와 거의 유사하지만, NaN
과 NaN
을 같다고 취급하는 것에서 일치 연산자와 차이가 있다.
👉 맵에선 NaN
도 key로 쓸 수 있다
map.set
호출 시 마다 맵 자신이 반환 👉 이를 이용하여 map.set
을 chaining
map.set('1', 'str1')
.set(1, 'num1')
.set(true, 'bool1')
값이 삽입된 순서대로 순회를 실시한다. 객체가 프로퍼티 순서를 기억하지 못하는 것과는 다르다.
중복을 허용하지 않는 값을 모아놓은 컬렉션, key가 없는 값이 저장된다.
new Set(iterable)
: Set을 만듦. iterable 객체를 전달받으면(대개 배열) 그 안의 값을 복사해 Set에 넣어줌set.add(value)
: 값을 추가하고 Set 자신을 반환set.delete(value)
: 값 제거하고 호출 시점에 Set 내에 값이 있어서 제거에 성공하여 true, 없으면 false를 반환set.has(value)
: Set 내에 값 존재하면 true, 아니면 false 반환set.clear()
: Set을 비움set.size
: Set에 몇 개의 값이 있는지 카운트// 방문자가 여러 번 방문해도 중복해서 기록하지 않으려 할 때
let set = new Set();
let john = { name: 'John' };
let pete = { name: 'Pete' };
let mary = { name: 'mary' };
set.add(john);
set.add(pete);
set.add(mary);
set.add(john);
set.add(pete);
// Set에는 중복저장이 되지 않는다
alert(set.size); // 3
for (let user of set) {
alert(user.name); // John, Pete, Mary 순으로 출력
}
JavaScript 엔진은 도달 가능한 (그리고 추후 사용될 가능성이 있는)값을 메모리에 유지한다
// 아래 객체는 jinju라는 참조를 통해 접근할 수 있다
let jinju = { name: 'Jinju' };
// 그런데 참조를 null로 덮어쓰면 위 객체에 더 이상 도달 불가능하므로 객체가 메모리에서 삭제된다
jinju = null;
자료구조를 구성하는 요소도 자신이 속한 자료구조가 메모리에 남아있는 동안 대개 도달 가능한 값으로 취급되어 메모리에서 삭제되지 않는다.
객체의 프로퍼티나 배열의 요소, 맵이나 셋을 구성하는 요소들이 이에 해당한다.
let jinju = { name: 'Jinju' };
let arr = [ jinju ];
jinju = null; // 참조를 null로 덮어 씀
// jinju를 나타내는 객체는 배열의 요소이기 때문에 가비지 컬렉터의 대상이 되지 않는다
// arr[0]을 이용해서 해당 객체를 얻을 수도 있다
alert(JSON.stringify(arr[0]);
map과 weakmap의 첫 번째 차이는 weakmap의 key가 반드시 객체여야 한다는 점이다
원시값은 위크맵의 key가 될 수 없다
let weakMap = new WeakMap();
let obj = {};
weakMap.set(obj, 'ok'); // 정상적으로 동작한다 (객체 key)
weakMap.set('test', 'notOk'); // Error: Invalid value used as weak map key
weakmap의 key로 사용된 객체를 참조하는 것이 아무것도 없다면 해당 객체는 메모리와 weakmap에서 자동으로 삭제된다
let jinju = { name: 'Jinju' };
let weakMap = new WeakMap();
weakMap.set(jinju, '...');
jinju = null; // 참조를 덮어씀
// jinju를 나타내는 객체는 이제 메모리에서 지워진다
jinju
를 나타내는 객체는 오로지 weakmap의 key로만 사용되고 있으므로, 참조를 덮어쓰게 되면 이 객체는 위크맵과 메모리에서 자동으로 삭제된다.
map과 weakmap의 두 번째 차이는 weakmap은 반복 작업과 keys()
, values()
, entries()
메서드를 지원하지 않는다는 점 👉 weakmap에서는 key나 값 전체를 얻는 것이 불가능하다
weakMap.get(key)
weakMap.set(key, value)
weakMap.delete(key)
weakMap.has(key)
e.g.) 서드파티 라이브러리처럼 외부 코드에 속한 객체를 가지고 작업해야하는 상황 > 이 객체에 데이터를 추가해야하는데 추가해 줄 데이터는 객체가 살아있는 동안에만 유효 > 이럴 때 weakmap을 활용
weakmap에 원하는 데이터를 저장하고, 이 때 key는 객체를 사용한다 👉 객체가 가비지 컬렉션의 대상이 될 때 데이터도 함께 사라지게 된다
weakMap.set(john, '비밀문서');
// john 사망 시, 비밀문서는 자동으로 파기
e.g.) 사용자의 방문횟수를 세어주는 코드. 관련 정보는 map에 저장하는데 map 요소의 key에는 특정 사용자를 나타내는 객체를, 값엔 해당 사용자의 방문 횟수를 저장한다.
특정 사용자의 정보를 저장할 필요가 없어지면 (= 가비지 컬렉션의 대상이 되면) 해당 사용자의 방문 횟수도 저장할 필요가 없어진다
let visitsCountMap = new Map(); // map에 사용자 방문횟수 저장
function countUser(user) {
let count = visitsCountMap.get(user) || 0;
visitsCountMap.set(user, count + 1);
}
let john = { name: 'John' };
countUser(john); // John의 방문횟수를 증가시킴
// John의 방문 횟수를 셀 필요가 없어지면 아래와 같이 john을 null로 덮어씀
john = null;
john
을 나타내는 객체는 가비지 컬렉션의 대상이 되어야하는데, visitsCountMap
의 key로 사용되고 있어서 메모리에서 삭제되지 않는다
특정 사용자를 나타내는 객체가 메모리에서 사라지면 해당 객체에 대한 정보도 손수 지워줘야 하는 문제
👉 weakmap을 활용해서 예방할 수 있다
let visitsCountMap = new WeakMap(); // weakmap에 사용자의 방문 횟수를 저장
function countUser(user) {
let count = visitsCountMap.get(user) || 0;
visitsCountMap.set(user, count + 1);
}
weakmap을 사용해서 사용자 방문횟수를 저장하면 visitsCountMap
을 수동으로 청소해줄 필요가 없다 > john
을 나타내는 객체가 도달 불가능 상태가 되면 자동으로 메모리에서 삭제되므로 weakmap의 key에 대응하는 값인 john의 방문 횟수도 자동으로 가비지 컬렉션의 대상이 된다
Caching❓: 시간이 오래 걸리는 작업의 결과를 저장해서 연산 시간과 비용을 절약하는 기법
e.g.) 동일한 함수를 여러 번 호출해야 할 때, 최초 호출 시 반환된 값을 어딘가 저장했다가 그 다음에 함수를 호출하는 대신 저장된 값을 사용
// 📁 cache.js
let cache = new WeakMap();
// 연산 수행 후 그 결과를 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;
add
,has
,delete
를 사용가능하고 size
, keys()
나 반복작업 관련 메서드는 사용 불가let visitedSet = new WeakSet();
let john = { name: 'John' };
let pete = { name: 'Pete' };
let mary = { name: 'Mary' };
visitedSet.add(john);
visitedSet.add(pete);
visitedSet.add(john);
// john 은 두 번 방문했지만
// visitedSet에는 두 명의 사용자가 저장된다
// John의 방문여부 확인하기
alert(visitedSet.has(john)); // true
// Mary의 방문여부 확인하기
alert(visitedSet.has(mary)); // false
// visitedSet에서 john을 나타내는 객체가 자동으로 삭제됨
john = null;
둘의 가장 큰 단점은 반복 작업이 불가능하다는 점
주 용도는 객체와 함께 '추가'데이터를 저장하는데 쓰임
weakmap은 map과 유사한 컬렉션이고, weakmap을 구성하는 요소의 key는 오직 객체만 가능하고 key로 사용된 객체가 메모리에서 삭제되면 이에 대응하는 값까지 삭제된다
weakSet은 set과 유사한 컬렉션이고, 객체만 저장할 수 있다. weakset에 저장된 객체가 도달 불가능한 상태가 되면 해당 객체는 메모리에서 삭제된다