Map

Baseline Widely available

This feature is well established and works across many devices and browser versions. It’s been available across browsers since July 2015.

Map 객체는 키-값 쌍과 키의 원래 삽입 순서를 기억합니다. 모든 값(객체 및 원시 값 모두)은 키 또는 값으로 사용될 수 있습니다.

시도해보기

설명

Map 객체는 키-값 쌍인 집합입니다. 한 Map에서의 키는 오직 단 하나만 존재 합니다. 이는 Map 집합의 유일성입니다. Map 객체는 키-값 쌍으로 반복됩니다. for...of 루프는 각 반복에 대해 [key, value]로 이루어진 멤버가 2개인 배열을 반환합니다. 반복은 삽입한 순서대로 발생합니다. 즉, set() 메서드로 맵에 처음 삽입한 각각의 키-값 쌍 순서와 대응됩니다. (set()이 호출되었을때 맵에서 해당 키가 없었을 경우입니다)

Map의 명세는 "평균적으로 집합 내 요소의 수에 따라 하위 선형인 접근 시간을 제공하는" 맵을 구현해야 한다고 기술되어 있습니다. 따라서 복잡성이 O(N)보다 더 나은 경우 내부적으로 해시 테이블(O(1) 룩업), 검색 트리(O(log(N)) 룩업) 또는 기타 데이터 구조로 표현될 수 있습니다.

키 동일성

값 동일성은 SameValueZero을 기반으로 합니다.(0-0을 다르게 취급하는 등가 같음을 사용하였습니다. 브라우저 호환성을 확인해보세요). 즉, NaN !== NaN에도 불구하고 NaNNaN과 동일하게 간주되며, 다른 모든 값은 === 연산자의 의미론에 따라 동일하게 간주됩니다.

객체 vs 맵

ObjectMap과 유사합니다. 둘 다 값을 가리키는 키를 설정하고, 해당 값을 받아오며, 키를 삭제하며, 키에 무언가가 저장되었는지 여부를 알 수 있습니다. 이러한 이유로 (그리고 대안으로 사용할 수 있는 내장 객체가 없었기 때문에) Object는 역사적으로 Map으로 사용되어 왔습니다.

그러나 경우에 따라서는 아래와 같이 Map이 선호되는 중요한 차이점이 있습니다.

Map Object
우발적 키 Map는 기본적으로 키를 포함하지 않습니다. 여기에는 명시적으로 입력된 내용만 포함됩니다.

Object는 프로토타입이 있으므로 주의하지 않으면 자신의 키와 충돌할 수 있는 기본 키가 포함되어 있습니다.

참고: 이는 Object.create(null)를 사용하여 무시할 수 있지만 거의 수행되지 않습니다.

보안 Map은 사용자가 제공하는 키와 값에 대해서 안전하게 사용할 수 있습니다.

사용자가 제공한 키-값 쌍을 Object에 설정하면 공격자가 객체의 프로토타입을 재정의하여 객체 주입 공격을 발생시킬 수 있습니다. 돌발적인 키 문제와 마찬가지로 null 프로토타입 객체를 사용하여 이 문제를 해결할 수도 있습니다.

키 유형 Map의 키는 모든 값(함수, 객체 또는 원시값 포함)이 될 수 있습니다. Object의 키는 String 또는 Symbol이여야 합니다.
키 순서

Map에서 키는 단순하고 직관적인 방식으로 정렬됩니다. Map 객체는 항목을 삽입한 순서대로 항목, 키 및 값을 반복합니다.

일반적인 Object의 키는 정렬되어 있지만 항상 그런것은 아니며 순서가 복잡합니다. 결과적으로 속성 순서에 의존하지 않는 것이 가장 좋습니다.

순서는 ECMAScript 2015에서만 자체 속성에 대해 처음 정의되었습니다. ECMAScript 2020은 상속된 속성의 순서도 정의합니다. OrdinaryOwnPropertyKeysEnumerateObjectProperties 추상 명세작업을 참고하시기 바랍니다. 그러나 객체의 모든 속성을 반복하는 단일 메커니즘은 없습니다. 다양한 메커니즘에는 각각 다른 속성 하위 집합이 포함됩니다 (for-in은 오직 열거 가능한 문자열 키 속성만 포함합니다. Object.keys은 열거 가능한 자체 문자열 키 속성만 포함합니다. Object.getOwnPropertyNames은 열거 불가능하더라도 자체 문자열 키 속성을 포함합니다. Object.getOwnPropertySymbolsSymbol-키 속성에 대해 동일합니다.)

크기

Map의 아이템의 수는 size 속성에서 쉽게 가져올 수 있습니다. Object의 아이템 수는 수동으로 결정해야 합니다.
순회 Map 순회가능(iterable)하기 때문에 이므로 직접 반복할 수 있습니다.

Objectiteration protocol을 구현하지 않기 때문에 로 개체는 for...of문을 사용하여 직접적으로 반복할 수 없습니다.

참고:

  • 객체는 iteration protocol을 구현하거나 Object.keys 혹은 Object.entries 를 사용하여 객체의 반복 가능 항목을 얻을 수 있습니다.
  • for...in 문을 사용하면 객체의 열거 가능한 속성(enumerable)을 반복할 수 있습니다 .
성능

키-값 쌍의 빈번한 추가 및 제거와 관련된 상황에서는 성능이 좀 더 좋습니다.

키-값 쌍의 빈번한 추가 및 제거에 최적화되지 않았습니다.

Serialization and parsing

직렬화 또는 구문 분석에 대한 기본 지원이 없습니다.

(하지만 JSON.stringify()에 인자로 replacer를 사용한다면 Map에 대한 자신만의 직렬화 및 구문 분석을 지원하게 만들 수 있습니다. Stack Overflow 질문인 How do you JSON.stringify an ES6 Map?)을 참고하세요.)

JSON.stringify()를 사용하여 Object을 JSON으로 직렬화를 기본 지원합니다.

JSON.parse()를 사용하여 JSON에서 Object으로의 구문 분석을 기본 지원합니다.

객체 속성 설정하기

객체 속성 설정은 Map 객체에서도 작동하지만 상당한 혼란을 야기할 수 있습니다.

따라서 이는 다음과 같은 방식으로 동작하는 것처럼 보입니다.

js
const wrongMap = new Map();
wrongMap["bla"] = "blaa";
wrongMap["bla2"] = "blaaa2";

console.log(wrongMap); // Map { bla: 'blaa', bla2: 'blaaa2' }

그러나 속성을 설정하는 방법은 Map 데이터 구조와 상호 작용하지 않습니다. 속성 설정은 일반 객체의 기능을 사용합니다. 'bla' 값은 질의를 위한 Map에 저장되지 않습니다. 데이터에 대한 다른 작업도 아래와 같이 실패합니다.

js
wrongMap.has("bla"); // false
wrongMap.delete("bla"); // false
console.log(wrongMap); // Map { bla: 'blaa', bla2: 'blaaa2' }

맵에 데이터를 저장하는 올바른 방법은 set(key, value) 메서드를 사용하는 것입니다.

js
const contacts = new Map();
contacts.set("Jessie", { phone: "213-555-1234", address: "123 N 1st Ave" });
contacts.has("Jessie"); // true
contacts.get("Hilary"); // undefined
contacts.set("Hilary", { phone: "617-555-4321", address: "321 S 2nd St" });
contacts.get("Jessie"); // {phone: "213-555-1234", address: "123 N 1st Ave"}
contacts.delete("Raymond"); // false
contacts.delete("Jessie"); // true
console.log(contacts.size); // 1

생성자

Map()

새로운 Map 객체를 생성합니다.

정적 속성

get Map[@@species]

파생 객체를 만드는 데 사용되는 생성자 함수입니다.

인스턴스 속성

Map.prototype[@@toStringTag]

@@toStringTag 속성의 초기 값은 문자열 "Map"입니다. 이 속성은 Object.prototype.toString()에서 사용합니다.

Map.prototype.size

Map 객체의 키/값 쌍의 숫자를 반환합니다.

인스턴스 메서드

Map.prototype.clear()

Map 객체에서 모든 키-값 쌍을 제거합니다.

Map.prototype.delete()

Map 객체에 해당 요소가 존재하며 삭제되었을 경우 true를 반환하며 해당 요소가 존재하지 않는 경우 false를 반환합니다. 이 이후에 map.has(key)를 실행하면 false를 반환할 것입니다.

Map.prototype.get()

주어진 키에 해당하는 값을 반환하거나 값이 없다면 undefined을 반환합니다.

Map.prototype.has()

주어진 키에 연관된 값이 Map 객체에 존재하는지 여부를 불리언 값으로 반환합니다.

Map.prototype.set()

Map객체에서 전달된 키의 값을 설정합니다. Map객체를 반환합니다.

Map.prototype[@@iterator]()

Map 객체에 삽입된 순서에 따라 [key, value] 두 개의 멤버 배열을 포함하는 새로운 반복자를 반환합니다.

Map.prototype.keys()

Map 객체에 삽입된 순서에 따라 각 요소의 키를 포함하는 새로운 반복자를 반환합니다.

Map.prototype.values()

Map 객체에 삽입된 순서에 따라 각 요소의 값을 포함하는 새로운 반복자를 반환합니다.

Map.prototype.entries()

Map 객체에 삽입된 순서에 따라 [key, value] 두 개의 멤버 배열을 포함하는 새로운 반복자를 반환합니다.

Map.prototype.forEach()

Map객체에 존재하는 각 키-값 쌍에 대해 삽입 순서대로 callbackFn을 한 번씩 호출 합니다. 만약 forEach호출 시 thisArg 매개변수가 있다면 각 콜백의 this 값으로 사용됩니다.

예제

Map 객체 사용하기

js
const myMap = new Map();

const keyString = "a string";
const keyObj = {};
const keyFunc = function () {};

// 값 설정
myMap.set(keyString, "value associated with 'a string'");
myMap.set(keyObj, "value associated with keyObj");
myMap.set(keyFunc, "value associated with keyFunc");

console.log(myMap.size); // 3

// 값 불러오기
console.log(myMap.get(keyString)); // "value associated with 'a string'"
console.log(myMap.get(keyObj)); // "value associated with keyObj"
console.log(myMap.get(keyFunc)); // "value associated with keyFunc"

console.log(myMap.get("a string")); // "value associated with 'a string'", 왜냐하면 keyString === 'a string'
console.log(myMap.get({})); // undefined, 왜냐하면 keyObj !== {}
console.log(myMap.get(function () {})); // undefined, 왜냐하면 keyFunc !== function () {}

Map 키로 NaN 사용하기

NaN도 키로 사용할 수 있습니다. 모든 NaN이 자신과 같지는 않지만(NaN !== NaN은 참입니다), NaN은 서로 구별할 수 없기 때문에 다음과 같은 예시가 동작합니다.

js
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로 맵 순회하기

맵은 for...of 반복을 사용하여 순회할 수 있습니다.

js
const myMap = new Map();
myMap.set(0, "zero");
myMap.set(1, "one");

for (const [key, value] of myMap) {
  console.log(`${key} = ${value}`);
}
// 0 = zero
// 1 = one

for (const key of myMap.keys()) {
  console.log(key);
}
// 0
// 1

for (const value of myMap.values()) {
  console.log(value);
}
// zero
// one

for (const [key, value] of myMap.entries()) {
  console.log(`${key} = ${value}`);
}
// 0 = zero
// 1 = one

forEach()로 맵 순회하기

맵은 forEach() 메서드를 사용하여 순회할 수 있습니다.

js
myMap.forEach((value, key) => {
  console.log(`${key} = ${value}`);
});
// 0 = zero
// 1 = one

배열 객체와의 관계

js
const kvArray = [
  ["key1", "value1"],
  ["key2", "value2"],
];

// Use the regular Map constructor to transform a 2D key-value Array into a map
const myMap = new Map(kvArray);
console.log(myMap.get("key1")); // "value1"

// Use Array.from() to transform a map into a 2D key-value Array
console.log(Array.from(myMap)); // Will show you exactly the same Array as kvArray

// A succinct way to do the same, using the spread syntax
console.log([...myMap]);

// Or use the keys() or values() iterators, and convert them to an array
console.log(Array.from(myMap.keys())); // ["key1", "key2"]

맵 복제하기와 병합하기

Array와 같이 Map은 복제가 가능합니다.

js
const original = new Map([[1, "one"]]);

const clone = new Map(original);

console.log(clone.get(1)); // one
console.log(original === clone); // false (useful for shallow comparison)

참고 : 데이터 자체는 복제가 안된다는 사실을 명심하셔야 합니다.

맵은 키 유일성을 유지한채로 병합이 가능합니다.

js
const first = new Map([
  [1, "one"],
  [2, "two"],
  [3, "three"],
]);

const second = new Map([
  [1, "uno"],
  [2, "dos"],
]);

// 두 맵을 병합합니다. 키 값이 중복될 경우 마지막 키의 값을 따릅니다.
// 전개 문법은 맵을 배열로 변환합니다.
const merged = new Map([...first, ...second]);

console.log(merged.get(1)); // uno
console.log(merged.get(2)); // dos
console.log(merged.get(3)); // three

맵은 배열과 병합될 수 있습니다.

js
const first = new Map([
  [1, "one"],
  [2, "two"],
  [3, "three"],
]);

const second = new Map([
  [1, "uno"],
  [2, "dos"],
]);

// 맵을 배열로 병합하기. 중복된 키가 있다면 마지막에 병합된 맵의 키에 해당하는 값이 설정됩니다.
const merged = new Map([...first, ...second, [1, "eins"]]);

console.log(merged.get(1)); // eins
console.log(merged.get(2)); // dos
console.log(merged.get(3)); // three

명세

Specification
ECMAScript Language Specification
# sec-map-objects

브라우저 호환성

BCD tables only load in the browser

같이 보기