JavaScript/모던 자바스크립트 딥다이브
[JS] Map
김춘삼씨의 고양이
2024. 8. 18. 18:42
📌 Map 객체
- Map 객체: 키와 값의 쌍으로 이루어진 컬렉션
객체와 Map 객체의 차이 | 객체 | Map 객체 |
키로 사용할 수 있는 값 | 문자열 또는 심벌 값 | 객체를 포함한 모든 값 |
이터러블 | X | O |
요소 개수 확인 | Object.keys(obj).length | map.size |
📌 Map 객체의 생성
- Map 객체는 Map 생성자 함수로 생성함
- Map 생성자 함수에 인수를 전달하지 않으면 빈 Map 객체가 생성됨
- Map 생성자 함수는 이터러블을 인수로 전달받아 Map 객체를 생성하며, 이때 인수로 전달되는 이터러블은 키와 값의 쌍으로 이루어진 요소로 구성되어야 함
- Map 객체에는 중복된 키를 갖는 요소가 존재할 수 없음
// Map 객체의 생성
const map1 = new Map();
console.log(map1); // Map(0) {size: 0}
// Map 생성자 함수는 이터러블을 인수로 전달받음
const map2 = new Map([['key1', 'value1'], ['key2', 'value2']]);
console.log(map2); // Map(2) {'key1' => 'value1', 'key2' => 'value2'}
const map3 = new Map([1, 2]); // TypeError: Iterator value 1 is not an entry object
// Map 객체에는 중복된 키를 갖는 요소가 존재할 수 없음
const map4 = new Map([['key1', 'value1'], ['key1', 'value2']]);
console.log(map4); // Map(1) {'key1' => 'value2'}
📌 요소 개수 확인
- Map.prototype.size 프로퍼티를 사용해 Map 객체의 요소의 개수를 확인할 수 있음
- size 프로퍼티는 setter 함수 없이 getter 함수만 존재하는 접근자 프로퍼티이기 때문에 size 프로퍼티에 숫자를 할당하여 Map 객체의 요소 개수를 변경할 수 없음
const mapObj = new Map([['key1', 'value1'], ['key2', 'value2']]);
console.log(mapObj.size); // 2
mapObj.size = 10; // 무시됨
console.log(mapObj.size); // 2
📌 요소 추가
- Map.prototype.set 메서드를 사용해 Set 객체에 요소를 추가할 수 있음
- set 메서드는 새로운 요소가 추가된 Map 객체를 반환하기 때문에 set 메서드를 호출한 후에 set 메서드를 연속적으로 호출할 수 있음
- Map 객체에는 중복된 키를 갖는 요소가 존재할 수 없기 때문에 중복된 키를 갖는 요소를 추가하면 값이 덮어써짐
- Map 객체는 키 타입에 제한이 없기 때문에 객체를 포함한 모든 값을 키로 사용할 수 있음
- Map 객체는 일치 비교 연산자(===)와 달리 NaN과 NaN을 같다고 평가하여 중복 추가를 허용하지 않음
- +0과 -0은 일치 비교 연산자(===)와 마찬가지로 같다고 평가하여 중복 추가를 허용하지 않음
const mapObj = new Map();
console.log(mapObj); // Map(0) {size: 0}
// 요소 추가
mapObj.set('key1', 'value1');
console.log(mapObj); // Map(1) {'key1' => 'value1'}
// 요소 연속 추가
mapObj.set('key2', 'value2').set('key3', 'value3');
console.log(mapObj);
// Map(3) {'key1' => 'value1', 'key2' => 'value2', 'key3' => 'value3'}
// Map 객체는 모든 값을 키로 사용할 수 있음
const lee = { name: 'Lee' };
const kim = { name: 'Kim' };
mapObj.set(lee, 'dev').set(kim, 'design');
console.log(mapObj);
// Map(5) {'key1' => 'value1', 'key2' => 'value2', 'key3' => 'value3', {name: 'Lee'} => 'dev', {name: 'Kim'} => 'design'}
📌 요소 취득
- Map.prototype.get 메서드를 사용해 Map 객체에서 특정 요소를 취득할 수 있음
- get 메서드의 인수로 키를 전달하면 Map 객체에서 인수로 전달한 키를 갖는 값을 반환함
(요소가 존재하지 않으면 undefined를 반환)
const mapObj = new Map();
const lee = { name: 'Lee' };
const kim = { name: 'Kim' };
mapObj.set(lee, 'dev').set(kim, 'design');
// 요소 취득
console.log(mapObj.get(lee)); // dev
console.log(mapObj.get('key')); // undefined
📌 요소 존재 여부 확인
- Map.prototype.has 메서드를 사용해 Map 객체에 특정 요소가 존재하는지 확인할 수 있음
- has 메서드는 특정 요소의 존재 여부를 나타내는 불리언 값을 반환함
const lee = { name: 'Lee' };
const kim = { name: 'Kim' };
const mapObj = new Map([[lee, 'dev'], [kim, 'design']]);
// 요소 존재 여부 확인
console.log(mapObj.has(lee)); // true
console.log(mapObj.has('key')); // false
📌 요소 삭제
- Map.prototype.delete 메서드를 사용해 Map 객체의 특정 요소를 삭제할 수 있음
- delete 메서드는 삭제 성공 여부를 나타내는 불리언 값을 반환하기 때문에 연속적으로 호출할 수 없음
const lee = { name: 'Lee' };
const kim = { name: 'Kim' };
const mapObj = new Map([[lee, 'dev'], [kim, 'design']]);
mapObj.delete(kim);
console.log(mapObj); // Map(1) {{name: 'Lee'} => 'dev'}
// 존재하지 않는 요소 삭제 시 에러 없이 무시됨
mapObj.delete('key');
console.log(mapObj); // Map(1) {{name: 'Lee'} => 'dev'}
📌 요소 일괄 삭제
- Map.prototype.clear 메서드를 사용해 Map 객체의 모든 요소를 일괄 삭제할 수 있음
- clear 메서드는 언제나 undefined를 반환함
const lee = { name: 'Lee' };
const kim = { name: 'Kim' };
const mapObj = new Map([[lee, 'dev'], [kim, 'design']]);
// 요소 일괄 삭제
mapObj.clear();
console.log(mapObj); // Map(0) {size: 0}
📌 요소 순회
- Map.prototype.forEach 메서드를 사용해 Map 객체에 요소를 순회할 수 있음
- Map.prototype.forEach 메서드는 Array.prototype.forEach 메서드와 유사하게 콜백 함수와 forEach 메서드의 콜백 함수 내부에서 this로 사용될 객체(옵션)를 인수로 전달함
- 첫번째 인수: 현재 순회 중인 요소값
- 두 번째 인수: 현재 순회 중인 요소키
- 세 번째 인수: 현재 순회 중인 Map 객체 자체 - Map 객체는 이터러블이기 때문에 for ... of 문으로 순회할 수 있으며, 스프레드 문법과 배열 디스트럭처링의 대상이 될 수 있음
- Map 객체는 요소의 순서에 의미를 갖지 않지만 Map 객체를 순회하는 순서는 요소가 추가된 순서를 따름
const lee = { name: 'Lee' };
const kim = { name: 'Kim' };
const mapObj = new Map([[lee, 'dev'], [kim, 'design']]);
/*
v: 현재 순회 중인 요소값
k: 현재 순회 중인 요소키
thisMap: 현재 순회 중인 Map 객체 자체
*/
mapObj.forEach((v, k, thisMap) => console.log(v, k, thisMap));
/*
dev {name: 'Lee'} Map(2) {{name: 'Lee'} => 'dev', {name: 'Kim'} => 'design'}
design {name: 'Kim'} Map(2) {{name: 'Lee'} => 'dev', {name: 'Kim'} => 'design'}
*/
- Map 객체는 이터러블이면서 동시에 이터레이터인 객체를 반환하는 메서드를 제공함
Map 메서드 | 설명 |
Map.prototype.keys | Map 객체에서 요소키를 값으로 갖는 이터러블이면서 동시에 이터레이터인 객체를 반환함 |
Map.prototype.values | Map 객체에서 요소값을 값으로 갖는 이터러블이면서 동시에 이터레이터인 객체를 반환함 |
Map.prototype.entries | Map 객체에서 요소키와 요소값을 값으로 갖는 이터러블이면서 동시에 이터레이터인 객체를 반환함 |
const lee = { name: 'Lee' };
const kim = { name: 'Kim' };
const mapObj = new Map([[lee, 'dev'], [kim, 'design']]);
// keys 메서드
for (const key of mapObj.keys()) {
console.log(key); // {name: 'Lee'} {name: 'Kim'}
}
// valued 메서드
for (const value of mapObj.values()) {
console.log(value); // dev design
}
// entries 메서드
for (const entry of mapObj.entries()) {
console.log(entry); // [{name: 'Lee'}, 'dev'] [{name: 'Kim'}, 'design']
}
참고문헌 및 출처 : 모던 자바스크립트 Deep Dive (이웅모)