Зачем нам Map, когда у нас есть Object в JavaScript?

внешний интерфейс JavaScript
Зачем нам Map, когда у нас есть Object в JavaScript?

Мало знаний, большой вызов! Эта статья участвует в "Необходимые знания для программистов«Творческая деятельность.

Как мы все знаем,Mapиспользуется для хранения пар ключ-значение, а объекты в JavaScript также состоят из пар ключ-значение, тоMapВ чем смысл существования?

Не принимайте объекты за карту

1. Неопределенные свойства могут быть доступны через цепочку прототипов.

Предполагая существующий сценарий, для разработки веб-сайта вам необходимо предоставить три языка: японский, китайский и корейский.Мы можем определить словарь для управления им.

const dictionary = {
    'ja': {
        'Ninjas for hire': '忍者を雇う',
    },
    'zh': {
        'Ninjas for hire': '忍者出租',
    },
    'ko': {
        'Ninjas for hire': '고용 닌자',
    }
}

console.log(dictionary.ja['Ninjas for hire']) // 忍者を雇う
console.log(dictionary.zh['Ninjas for hire']) // 忍者出租
console.log(dictionary.ko['Ninjas for hire']) // 고용 닌자

Таким образом мы управляем словарями разных языков. Однако проблема возникает, когда мы пытаемся получить доступ к свойству controctor.

console.log(dictionary.ko['constructor']) // ƒ Object() { [native code] }

Для несуществующих свойств мы ожидаем получитьundefined, Результат осуществляется через цепочку прототипа до неопределенного свойства, объект прототипаconstructorсвойство, указывающее на конструктор.

Одним из решений здесь является установка прототипа наnull

Object.setPrototypeOf(dictionary.ko, null)
console.log(dictionary.ko['constructor']) // undefined

keyСопоставленный HTML-узел. Пишем следующий код:

/* html部分
<div id="firstElement"></div>
<div id="secondElement"></div>
*/

const firstElement = document.getElementById('firstElement')
const secondElement = document.getElementById('secondElement')

const map = {}

map[firstElement] = {
    data: 'firstElement'
}
map[secondElement] = {
    data: 'secondElement'
}

console.log(map[firstElement].data)     // secondElement
console.log(map[secondElement].data)    // secondElement

Данные первого элемента перезаписываются, причина в том, что в объектеkeyМожет быть только строковым типом, когда мы не используем строковый тип, он будет вызываться неявноtoString()функция для преобразования. Таким образом, оба элемента html преобразуются в строки.[object HTMLDivElement].

Объектные ключи также могут бытьSymbol, но вfor..inтраверс иObject.keys()и сJSON.stringify()При сериализации он будет игнорироваться какSymbolключ.

использовать карту

1. Сопоставьте общие операции

Карта может использовать любой тип данных JavaScript в качестве ключа

function People(name) {
    this.name = name
}
const zhangsan = new People('zhangsan')
const xiaoming = new People('xiaoming')
const lihua = new People('lihua')
// 创建 Map
const map = new Map()
// 创建 Map 并进行初始化 将二维键值对数组转换成一个Map对象
const map1 = new Map([
    ['key1', 'val1'],
    ['key2', 'val2'],
])
// 将 Map 转为二维数组
console.log(Array.from(map1)) // [ [ 'key1', 'val1' ], [ 'key2', 'val2' ] ]
// 设置键值映射关系
map.set(zhangsan, { region: 'HB' })
map.set(xiaoming, { region: 'HN' })
// 根据 key 获取对应值
console.log(map.get(zhangsan)) // { region: 'HB' }
console.log(map.get(xiaoming)) // { region: 'HN' }
// 获取不存在的 key 得到 undefined
console.log(map.get(lihua))     // undefined
// 通过 has 函数判断指定 key 是否存在
console.log(map.has(lihua))     // false
console.log(map.has(xiaoming))  // true
// map存储映射个数
console.log(map.size)           // 2
// delete 删除 key
map.delete(xiaoming)
console.log(map.has(xiaoming))  // false
console.log(map.size)           // 1
// clear 清空 map
map.clear()
console.log(map.size)           // 0

2. Перемещайтесь по карте

Карта может гарантировать, что порядок обхода соответствует порядку вставки

const zhangsan = { name: 'zhangsan' }
const xiaoming = { name: 'xiaoming' }
const map = new Map()
map.set(zhangsan, { region: 'HB' })
map.set(xiaoming, { region: 'HN' })
// 每个键值对返回的是 [key, value] 的数组
for (let item of map) { // = for (let item of map.entries()) {
    console.log(item)
    // [ { name: 'zhangsan' }, { region: 'HB' } ]
    // [ { name: 'xiaoming' }, { region: 'HN' } ]
}
// 遍历 key
for (let key of map.keys()) {
    console.log(key)
    // { name: 'zhangsan' }
    // { name: 'xiaoming' }
}
// 遍历 value
for (let key of map.values()) {
    console.log(key)
    // { region: 'HB' }
    // { region: 'HN' }
}
// 使用 forEach() 方法迭代 Map
map.forEach(function(value, key) {
    console.log(key, value)
    // { name: 'zhangsan' } { region: 'HB' }
    // { name: 'xiaoming' } { region: 'HN' }
})

3. Определите, что ключи равны в карте

Карта внутри использует операцию сравнения SameValueZero.

О SameValue и SameValueZero

SameValue (Object.is()) И строго равен (===) В сравнении сNaNа также+0,-0Различное лечение

Object.is(NaN, NaN) // true
Object.is(0, -0) // false

0а также-0Равенство.

map.set(NaN, 0)
map.set(0, 0)
console.log(map.has(NaN))   // true
console.log(map.has(-0))    // true

4. Скопируйте или объедините карту

  1. Карты можно копировать как массивы

    let original = new Map([
        [1, {}]
    ])
    let clone = new Map(original) // 克隆 Map
    
    console.log(clone.get(1)); // {}
    console.log(original === clone) // false
    console.log(original.get(1) === clone.get(1)) // true
    
  2. Объединение нескольких карт

    let first = new Map([
        [1, 'one'],
        [2, 'two'],
        [3, 'three'],
    ]);
    let second = new Map([
        [1, 'uno'],
        [2, 'dos']
    ]);
    // 合并两个 Map 对象时,如果有重复的键值,则后面的会覆盖前面的。
    // 展开运算符本质上是将 Map 对象转换成数组。
    let merged = new Map([...first, ...second]);
    
    console.log(merged.get(1)); // uno
    console.log(merged.get(2)); // dos
    console.log(merged.get(3)); // three
    

5. Сериализация карты

благодарный @Сердце рыбынапоминание.

Mapне может быть сериализован, если вы попытаетесь использоватьJSON.stringifyполучатьMapизJSON, вы получите только"{}".

из-заMapКлючи могут быть любого типа данных, в то время какJSONВ качестве ключей разрешены только строки, поэтому, как правило, невозможноMapПеревести вJSON.

Map:

// 初始化 Map(1) {"key1" => "val1"}
const originMap = new Map([['key1', 'val1']])
// 序列化 "[[\"key1\",\"val1\"]]"
const mapStr = JSON.stringify(Array.from(originMap.entries())) 
// 反序列化 Map(1) {"key1" => "val1"}
const cloneMap = new Map(JSON.parse(mapStr))

Карта объектов и разница в производительности

  1. использование памяти

MapObjectМультимагазин50%пара ключ/значение.

  1. Производительность вставки

MapНемного быстрее, рекомендуется, если задействовано много операцийMap.

  1. Найти скорость

Разница в производительности минимальна, но если включить только несколько пар ключ/значение, тоObjectИногда быстрее.ObjectБраузеры оптимизируют при использовании в качестве массива. Если задействовано большое количество поисковых запросов, выберитеObjectбыло бы лучше.

  1. удалить производительность

Если код предполагает большое количество операций удаления, рекомендуется выбратьMap.

использованная литература

  • Читы JavaScript Ninja (второе издание)
  • Расширенное программирование с помощью JavaScript (четвертое издание)
  • Map - MDN