Map
Map
Map 객체는 키-값 쌍의 컬렉션입니다. 각 키는 컬렉션에서 한 번만 나타날 수 있으며, 삽입 순서대로 반복됩니다. Object와 달리 어떤 값이든 키로 사용할 수 있고 size 속성으로 크기를 쉽게 얻을 수 있습니다.
본문
Map 객체는 키-값 쌍의 컬렉션입니다. Map의 키는 한 번만 나타날 수 있으며, 컬렉션에서 유일합니다. Map 객체는 키-값 쌍으로 반복됩니다. for...of 루프는 각 반복마다 [key, value] 두 멤버 배열을 반환합니다. 반복은 삽입 순서로 일어나는데, 이는 각 키-값 쌍이 set() 메서드로 처음 삽입된 순서와 일치합니다(즉, set() 호출 시점에 같은 값을 가진 키가 이미 없을 때).
사양은 Map이 "평균적으로 컬렉션의 요소 수에 대해 서브리니어(sublinear)한 접근 시간을 제공"하도록 구현할 것을 요구합니다. 따라서 해시 테이블(O(1) 조회), 검색 트리(O(log(N)) 조회), 또는 O(N)보다 나은 복잡도를 갖는 어떤 자료 구조든 내부적으로 사용할 수 있습니다.
키 동등성 (Key equality)
값 동등성은 SameValueZero 알고리즘에 기반합니다. (예전에는 0과 -0을 다르게 취급했던 SameValue를 사용했습니다.) 이는 NaN은 NaN과 같다고 간주되고(NaN !== NaN이어도), 그 외의 모든 값은 === 연산자의 의미에 따라 같다고 간주된다는 뜻입니다. 또한 객체 키의 경우 동등성은 객체 정체성(identity)에 기반합니다. 값으로 비교하는 것이 아니라 참조로 비교합니다.
객체 vs Map
Object는 Map과 유사합니다. 둘 다 키에 값을 설정하고, 값을 검색하고, 키를 삭제하고, 키에 저장된 것이 있는지 감지할 수 있습니다. 이러한 이유로(그리고 내장 대안이 없었기 때문에) 역사적으로 Object가 Map으로 사용되어 왔습니다. 그러나 몇 가지 중요한 차이점이 있어 어떤 경우에는 Map이 더 선호됩니다.
- 우발적 키(Accidental Keys) —
Map은 기본적으로 어떤 키도 포함하지 않습니다. 명시적으로 넣은 것만 포함합니다. 반면Object는 프로토타입을 가지므로, 조심하지 않으면 자신의 키와 충돌할 수 있는 기본 키를 포함합니다.Object.create(null)로 우회할 수 있지만 거의 쓰이지 않습니다. - 보안(Security) —
Map은 사용자 제공 키와 값에 안전하게 사용할 수 있습니다.Object에 사용자 제공 키-값 쌍을 설정하면 공격자가 객체의 프로토타입을 덮어쓸 수 있어 객체 주입 공격이나 프로토타입 오염(prototype pollution) 공격으로 이어질 수 있습니다. - 키 타입(Key Types) —
Map의 키는 어떤 값이든 될 수 있습니다(함수, 객체, 프리미티브 포함).Object의 키는String또는Symbol이어야 합니다. - 키 순서(Key Order) —
Map의 키는 직관적인 방식으로 정렬됩니다.Map객체는 항목, 키, 값을 삽입 순서대로 반복합니다. 반면 일반Object의 키도 이제는 정렬되지만 항상 그랬던 것은 아니고 순서가 복잡하여 속성 순서에 의존하지 않는 것이 좋습니다. (자체 속성에 대한 순서는 ECMAScript 2015, 상속 속성에 대한 순서는 ECMAScript 2020에 정의되었습니다.for...in,Object.keys,Object.getOwnPropertyNames,Object.getOwnPropertySymbols는 각각 서로 다른 속성 부분집합을 포함합니다.) - 크기(Size) —
Map의 항목 수는size속성에서 쉽게 얻습니다.Object의 항목 수는Object.keys()가 반환한 배열의 길이로 알아내는 것이 더 번거롭고 비효율적입니다. - 반복(Iteration) —
Map은 이터러블이므로 직접 반복할 수 있습니다.Object는 반복 프로토콜을 구현하지 않아 기본적으로for...of문으로 직접 반복할 수 없습니다. (객체에 반복 프로토콜을 구현하거나Object.keys,Object.entries로 이터러블을 얻을 수 있고,for...in으로 열거 가능한 속성을 반복할 수 있습니다.) - 성능(Performance) —
Map은 키-값 쌍의 빈번한 추가·제거 시나리오에서 더 좋은 성능을 보입니다.Object는 빈번한 추가·제거에 최적화되지 않았습니다. - 직렬화와 파싱(Serialization and parsing) —
Map은 직렬화·파싱 네이티브 지원이 없지만JSON.stringify()의 replacer 인자와JSON.parse()의 reviver 인자로 직접 지원을 구축할 수 있습니다.Object는JSON.stringify()/JSON.parse()로 네이티브 지원합니다.
객체 속성 설정 주의 — Map 객체에 wrongMap["bla"] = "blaa"처럼 속성을 설정하는 것은 Map 자료 구조와 상호작용하지 않습니다. 이는 일반 객체의 특성을 사용하는 것이며, wrongMap.has("bla")는 false를 반환합니다. Map에 데이터를 저장하는 올바른 방법은 set(key, value) 메서드를 사용하는 것입니다.
Map 같은 브라우저 API (Map-like) — 브라우저의 "maplike objects"는 여러 면에서 Map처럼 동작하는 Web API 인터페이스입니다. Map처럼 항목이 추가된 순서대로 반복할 수 있고 이름과 동작이 같은 속성·메서드를 가지지만, 각 항목의 키와 값에 특정 사전 정의 타입만 허용합니다. 예를 들어 RTCStatsReport는 키에 문자열, 값에 객체를 써야 하는 Map-like 객체입니다. 읽기 전용 Map-like 객체는 size 속성과 entries(), forEach(), get(), has(), keys(), values(), [Symbol.iterator]() 메서드를, 쓰기 가능한 것들은 추가로 clear(), delete(), set() 메서드를 가집니다. 예: AudioParamMap, EventCounts, KeyboardLayoutMap, RTCStatsReport 등.
생성자
Map()— 새Map객체를 만듭니다.
정적 속성
Map[Symbol.species]— 파생 객체를 만드는 데 사용되는 생성자 함수.
정적 메서드
Map.groupBy()— 제공된 콜백 함수가 반환하는 값들을 사용해 주어진 이터러블의 요소를 그룹화합니다. 반환되는Map은 테스트 함수의 고유 값들을 키로 사용하며, 각 그룹의 요소 배열을 얻는 데 사용할 수 있습니다.
인스턴스 속성
Map.prototype에 정의되어 모든 인스턴스가 공유하는 속성: constructor, size(키-값 쌍의 개수), [Symbol.toStringTag](초기 값은 "Map").
인스턴스 메서드
Map.prototype.clear()—Map객체의 모든 키-값 쌍을 제거합니다.Map.prototype.delete()— 키로 지정된 항목을 이Map에서 제거합니다.Map.prototype.entries()— 삽입 순서로 각 요소의[key, value]두 멤버 배열을 담은 새Iterator객체를 반환합니다.Map.prototype.forEach()— 삽입 순서로 각 키-값 쌍에 대해callbackFn을 한 번씩 호출합니다.thisArg가 제공되면 각 콜백의this값으로 사용됩니다.Map.prototype.get()— 이Map에서 키에 해당하는 값을 반환하거나, 없으면undefined를 반환합니다.Map.prototype.getOrInsert()— 지정된 키에 해당하는 값을 반환합니다. 키가 없으면 주어진 기본 값으로 새 항목을 삽입하고 삽입된 값을 반환합니다.Map.prototype.getOrInsertComputed()— 지정된 키에 해당하는 값을 반환합니다. 키가 없으면 주어진 콜백으로 계산한 기본 값으로 새 항목을 삽입하고 그 값을 반환합니다.Map.prototype.has()— 지정된 키의 항목이 존재하는지 여부를 나타내는 불리언을 반환합니다.Map.prototype.keys()— 삽입 순서로 각 요소의 키를 담은 새Iterator객체를 반환합니다.Map.prototype.set()— 지정된 키와 값으로 새 항목을 이Map에 추가하거나, 키가 이미 존재하면 기존 항목을 갱신합니다.Map.prototype.values()— 삽입 순서로 각 요소의 값을 담은 새Iterator객체를 반환합니다.Map.prototype[Symbol.iterator]()— 삽입 순서로 각 요소의[key, value]배열을 담은 새Iterator객체를 반환합니다(entries()와 동일).
예제
Map 객체 사용 — 키로 문자열, 객체, 함수를 모두 사용할 수 있습니다.
const myMap = new Map();
const keyString = "a string";
const keyObj = {};
const keyFunc = () => {};
myMap.set(keyString, "value associated with 'a string'");
myMap.set(keyObj, "value associated with keyObj");
myMap.set(keyFunc, "value associated with keyFunc");
myMap.get(keyString); // "value associated with 'a string'"
myMap.get(keyObj); // "value associated with keyObj"
myMap.get("a string"); // "value associated with 'a string'", keyString === 'a string' 이므로
myMap.get({}); // undefined, keyObj !== {} 이므로
myMap.get(() => {}); // undefined, keyFunc !== () => {} 이므로
NaN을 Map 키로 사용 — NaN은 자기 자신과 같지 않지만(NaN !== NaN), 모든 NaN이 서로 구별 불가능하므로 키로 사용할 수 있습니다.
const myMap = new Map();
myMap.set(NaN, "not a number");
myMap.get(NaN); // "not a number"
const otherNaN = Number("foo");
myMap.get(otherNaN); // "not a number"
for...of로 Map 반복 — Map은 for...of 루프로 반복할 수 있고, keys(), values(), entries()로 각각 키, 값, 항목을 순회할 수 있습니다.
더 알아보기
Set— 고유 값의 컬렉션WeakMap— 약한 참조 키를 가진 키-값 컬렉션Object— JavaScript의 일반 객체Using the Map object안내서