Map은 key, value로 이루어진 순서가 있는 컬렉션이다. 삽입 순서를 기억하며 key/value 추가/제거가 빈번할 때 객체보다 더 좋은 성능을 가진다 MDN - Map 페이지를 보면 아래와 같이 적혀있다.

Map : 키-값 쌍의 빈번한 추가 및 제거와 관련된 상황에서는 성능이 좀 더 좋습니다. Object : 키-값 쌍의 빈번한 추가 및 제거에 최적화되지 않았습니다.

벤치마크 출처 : Perf.link

벤치마크 출처 : Perf.link

실제로 랜덤 숫자 10,000개로 구성된 객체를 추가/삭제할 때 Map은 901 ops/s, 객체는 183 ops/s로 측정된다. 즉, 1초 동안 Map은 901번의 작업을 처리하고, 객체는 183번의 작업을 처리한다. Map이 일반 객체보다 거의 4~5배 가량 빠르다는 것을 의미한다. 물론 이 벤치마크를 100% 신뢰할 수 없지만 key/value 추가/제거에 있어 Map이 객체보다 최적화된 것은 자명해보인다.

<aside> <img src="/icons/search_gray.svg" alt="/icons/search_gray.svg" width="40px" /> ops/s는 초당 연산 횟수를 가리킨다

</aside>

내장 키


키가 없는 빈 객체라도 자바스크립트의 모든 객체는 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) 메서드를 이용하면 인자로 넘긴 프로퍼티가 객체 자신이 소유한 것인지 확인할 수 있다. ex) Object.hasOwn(myMap, valueOf)false

Map을 사용하면 이러한 문제에서 자유로울 수 있다.

반복


만약 생성자 함수의 프로토타입을 수정한 상황에서 for in으로 순회하면 자신이 소유하지 않은 프로퍼티도 출력한다. 이를 방지하기 위해 if (Object.hasOwn(...)) 같은 조건을 추가 할 수 있지만 번거롭다.

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 반복문 ➋전개 문법 ➌배열 구조분해 할당의 대상으로 사용할 수도 있다.*