Published on

Map & Set

Map

기존의 Object의 Key는 반드시 Symbol이나 문자열이어야 했다. 또한 prototype 체인 때문에 의도하지 않은 연결이 생길 수 있다.

Map에서는 기존 Object의 단점을 해결하였다. 객체를 키로 사용할 수 있으며, 의도치 않은 연결은 발생하지 않는다.

const symbol = Symbol();
const string2 = 'string2';
const regularObject = {
  string1: 'value1',
  [string2]: 'value2',
  [symbol]: 'value3',
};

const func = () => null;
const object = {};
const array = [];
const bool = false;
const map = new Map();
map.set(func, 'value1');
map.set(object, 'value2');
map.set(array, 'value3');
map.set(bool, 'value4');
map.set(NaN, 'value5');

Object는 직접적으로 iterate 할 수 없어서 다음 method들을 사용해서 key - value를 iterate 할 수 있다.

(Object.keys(), Object.values(), Object.entries(), for ... in loop)

또한 for ... in loop 에는 제약사항이 있는데, 순서가 보장되지 않고 Symbol key는 조회 할 수 없다.

Map에서는 순서를 보장하면서 아래 예제와 같이 직접 iterate 할 수 있다.

key - value 의 개수는 map.size 를 통해서 조회할 수 있다. (Object에서는 Object.keys().length)

for (let [key, value] of map) {
  console.log(key);
  console.log(value);
}
map.forEach((key, value) => {
  console.log(key);
  console.log(value);
});

Map과 Object의 성능

대량의 데이터를 순차적으로 set하고 get할 때 성능을 비교하면, Map의 set이 Object의 set보다는 약 2배정도 성능이 느리다. 그러나 get에서는 Map이 Object보다 10배 빠르다.

<Set성능>
Object의 경우 데이터가 순차적으로 적재되지 않는다.
브라우저는 Object에서 set활동이 일어나면 아무곳에 공간을 만들어서 값을 넣어버린다.
Map은 key-value 형식으로 구성되어 있으며 순차적으로 데이터를 적재한다.
여기서 발생되는 추가적인 활동(데이터 재배치, 정렬 등)이 set 처리에 대해 일부 성능저하를 발생시킨다.
<Get성능>
Object의 경우 Object.keys 를 사용하여 key 값을 별도로 가져와야하며,
각 key를 사용하여 데이터를 직접적으로 찾아와야한다.
그러나 map의 경우 순차적으로 놓여져 있는 데이터를 순회만 하면 되기 때문에 조회 속도 면에서는 우위를 가져간다.

WeakMap

WeakMap은 아래의 3가지의 차이점을 제외하면 맵과 같다.

  • 키는 반드시 객체여야 한다
  • WeakMap의 키는 가비지 콜렉션에 포함될 수 있다.
  • WeakMap은 이터러블이 아니며 Clear() 메서드도 없다.

JS는 코드 어딘가에서 객체를 참조하는 한 그 객체를 메모리에 계속 유지한다.

만약 Map의 키인 객체 o 가 존재한다면 Map이 존재하는 이상 객체 o도 메모리에 계속 유지된다.

WeakMap은 그렇지 않다. 따라서 이터러블이 될 수 없다. (가비지 컬렉션 중인 객체를 노출할 위험이 크기 때문)

이러한 특징들 덕에 WeakMap은 객체 인스턴스의 전용키를 저장하기에 알맞다.

const SecretHolder = (function () {
  const secrets = new WeakMap();
  return class {
    setSecret(secret) {
      secrets.set(this, secret);
    }
    getSecret() {
      return secrets.get(this);
    }
  };
})();

const a = new SecretHolder();
const b = new SecretHolder();

a.setSecret('secret A');
b.setSecret('secret B');

console.log(a.getSecret());
console.log(b.getSecret());

Set

Set은 중복을 허용하지 않는 데이터 집합이다. 동일한 값에 대해서 단 한번만 기록되어야 하는 경우에 사용된다.

const roles = new Set();

roles.add('1');
roles.add('xxx');

roles.size; // 2

roles.add('1');
roles.size; // 2. 중복이라면 아무일도 일어나지 않는다.

roles.delete('xxx');
roles; // Set ["1"]
roles.size; // 1
roles.delete('xxx'); // False

Set 또한 for...of나 forEach를 사용해서 iterate 할 수 있다.

let set = new Set(['oranges', 'apples', 'bananas']);
for (let value of set) alert(value);

set.forEach((value, valueAgain, set) => {
  alert(value);
});

forEach에서 첫번째 Parameter와 동일한 value를 두번째에서도(valueAgain) 전달받는다.

이렇게 구현된 이유는 Map과의 호환성 때문이다. 동일한 인터페이스를 제공하기 때문에 쉽게 Set을 Map으로 교체하기 쉽다.


참조