자바스크립트의 객체를 사용할 때 내용의 변경이 발생하는 key-value 값을 저장하는 경우 일반 객체 대신 map
을 활용하자!
const object = {};
object["key"] = 'value';
delete object["key"];
const map = new Map();
map.set('key', "value");
map.delete('key');
delete
연산자는 성능 저하로 이슈가 많지만 map
은 key
를 제거하는 데에 최적화가 되어 있어서 훨씬 빠르다고 합니다.map
이 key
를 자주 추가하거나 제거할 때 최적화가 되어있다는 것을 명확하게 설명하고 있습니다.map
성능이 뛰어난 이유는 자바스크립트 VM 엔진이 object를 최적화 하는 방법과 관련이 있습니다.key
를 추가하거나 제거하는데 좋은 해시맵과 같은 구조에는 최적화되어있지 않다고 합니다.key
-value
에 대응하기 위해 map
이 제작되었다고 합니다. ( hash 기반 )key
로 오염되어 있습니다.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]
// 의도하지 않은 일부 상속된 키가 등장할 수 있다.
for (const key in myObject) {
...
}
hasOwnProperty
를 사용합니다.for (const key in myObject) {
if (myObject.hasOwnProperty(key)) {
...
}
}
myObject.hasOwnProperty
를 재정의 한다면?? 문제가 발생할 수 있습니다!myObject.hasOwnProperty
를 사용하려면 아래와 같이 정의해야 합니다!// 점점 더티 코드가 되어가는...
for (const key in myObject) {
if (Object.prototype.hasOwnProperty.call(myObject, key) {
}
}
Object.keys(myObject).forEach(key => {
...
})
map
을 사용하면!!// map은 이터레이터를 제공하기 때문에 of를 사용할 수 있으며 key와 value를 한번에 가져올 수 있습니다!
for (const [key, value] of myMap) {
...
}
map
의 장점은 key
의 순서를 유지합니다!key
의 순서를 유지하지 않고 특정한 규칙으로 배치됩니다.key
를 분해할 수 있는 기능을 제공합니다.const [[firstKey, firstValue]] = myMap
// 펼치기 연산자
const copied = {...myObject}
// 객체 복사
const copied = Object.assign({}, myObject)
map
도 손쉽게 복사가 가능합니다.const copied = new Map(myMap)
map
의 깊은 복사본을 손쉽게 만들 수 있습니다.const deepCopy = structuredClone(myMap)
map
을 객체로 변환할 수 있습니다.const myObj = Object.fromEntries(myMap)
map
으로도 변환할 수 있습니다.const myMap = new Map(Object.entries(myObj))
map
을 활용할 때 아래와 같이 key
-value
형태로 직접 넣어서 생성합니다.const myMap = new Map([['key', 'value'], ['keyTwo', 'valueTwo']])
map
을 생성할 수 있습니다.const myMap = new Map(Object.entries({
key: 'value',
keyTwo: 'valueTwo',
}))
const makeMap = (obj) => new Map(Object.entries(obj))
const myMap = makeMap({ key: 'value' })
key
값에 대한 타입이 문자열 또는 심볼로 제한이 있습니다.key
값이 전달되면 내장된 toString
메소드로 형변환을 진행합니다.map
의 key
타입은 제한이 없고 모든 타입을 key
로 활용할 수 있습니다.myMap.set({}, value)
myMap.set([], value)
myMap.set(document.body, value)
myMap.set(function() {}, value)
myMap.set(myDog, value)
const metadata = new Map()
metadata.set(myDomNode, {
internalId: '...'
})
metadata.get(myDomNode)
// => { internalId: '...' }
map
이 참조하고 있는 데이터들은 가비지 컬렉터가 수집하지 않아 메모리 누수가 발생할 수 있습니다.const metadata = new Map()
metadata.set(myTodo, {
focused: true
})
metadata.get(myTodo)
// => { focused: true }
const metadata = new WeakMap()
// 해당 객체에 참조가 없으면 자동으로 myTodo가 map에서 제거 => 자동으로 가비지 컬렉터가 수집후 제거
metadata.set(myTodo, {
focused: true
})
map.clear() // map 전체 제거
map.size // map 사이즈 가져오기
map.keys() // map 모든 키의 이터레이터
map.values() // map 모든 값의 이터레이터
항상 map
과 세트로 언급되는 set
도 일반적인 배열보다 우수한 성능을 가지고 있습니다.
추가, 제거에 대해 배열보다 더 좋은 성능을 제공합니다. ( 해당 링크 참조 )
MDN 문서에서도 Set의 성능에 대한 내용을 공유하고 있습니다.
마찬가지로 WeakSet
도 존재하며 메모리 누수를 방지할 수 있습니다.
key
를 바탕으로 구조화된 데이터의 경우 일반적인 객체를 사용합니다.key
는 빠른 읽기 및 쓰기에 매우 최적화되어 있습니다.// 구조화된 데이터
const event = {
title: 'title',
date: new Date()
}
key
가 여러개 존재하고 key
를 자주 추가하거나 제거가 발생할 경우 map
을 활용합니다.const eventsMap = new Map()
eventsMap.set(event.id, event)
eventsMap.delete(event.id)
const myArray = [1, 2, 3, 2, 1]
set
를 활용합니다.const set = new Set([1, 2, 3])