Основное отличие состоит в том, что Объекты поддерживают только строковые ключи, тогда как Карты поддерживают более или менее любой тип ключа.
Если я это сделаю, obj[123] = true
то Object.keys(obj)
тогда я получу, ["123"]
а не [123]
. Карта будет сохранять тип ключа и возвращать, [123]
что здорово. Карты также позволяют использовать объекты в качестве ключей. Традиционно для этого вам нужно было бы дать объектам какой-то уникальный идентификатор для их хеширования (я не думаю, что когда-либо видел что-то подобное getObjectId
в JS как часть стандарта). Карты также гарантируют сохранение порядка, поэтому они лучше сохраняются и иногда могут спасти вас от необходимости делать несколько сортировок.
Между картами и объектами на практике есть несколько плюсов и минусов. Объекты получают как преимущества, так и недостатки, будучи очень тесно интегрированными в ядро JavaScript, что отличает их от значительного Map за исключением различий в поддержке ключей.
Непосредственным преимуществом является то, что у вас есть синтаксическая поддержка объектов, облегчающая доступ к элементам. У вас также есть прямая поддержка для этого с JSON. При использовании в качестве хэша раздражает получение объекта без каких-либо свойств. По умолчанию, если вы хотите использовать объекты в качестве хеш-таблицы, они будут загрязнены, и вам часто придется вызывать hasOwnProperty
их при доступе к свойствам. Здесь вы можете увидеть, как по умолчанию объекты загрязняются и как создавать, как мы надеемся, незагрязненные объекты для использования в качестве хэшей:
({}).toString
toString() { [native code] }
JSON.parse('{}').toString
toString() { [native code] }
(Object.create(null)).toString
undefined
JSON.parse('{}', (k,v) => (typeof v === 'object' && Object.setPrototypeOf(v, null) ,v)).toString
undefined
Загрязнение объектов не только делает код более раздражающим, медленным и т. Д., Но также может иметь потенциальные последствия для безопасности.
Объекты не являются чистыми хеш-таблицами, но пытаются сделать больше. У вас такие головные боли hasOwnProperty
, как неспособность легко определить длину ( Object.keys(obj).length
) и так далее. Объекты предназначены не только для использования в качестве хеш-карт, но и в качестве динамически расширяемых объектов, и поэтому при использовании их в качестве чистых хеш-таблиц возникают проблемы.
Сравнение / Список различных общих операций:
Object:
var o = {};
var o = Object.create(null);
o.key = 1;
o.key += 10;
for(let k in o) o[k]++;
var sum = 0;
for(let v of Object.values(m)) sum += v;
if('key' in o);
if(o.hasOwnProperty('key'));
delete(o.key);
Object.keys(o).length
Map:
var m = new Map();
m.set('key', 1);
m.set('key', m.get('key') + 10);
m.foreach((k, v) => m.set(k, m.get(k) + 1));
for(let k of m.keys()) m.set(k, m.get(k) + 1);
var sum = 0;
for(let v of m.values()) sum += v;
if(m.has('key'));
m.delete('key');
m.size();
Есть несколько других вариантов, подходов, методологий и т. Д. С различными подъемами и падениями (производительность, краткость, переносимость, расширение и т. Д.). Объекты немного странны, так как являются ядром языка, поэтому у вас есть много статических методов для работы с ними.
Помимо преимущества Карт, сохраняющих типы ключей, а также возможность поддерживать такие вещи, как объекты, в качестве ключей, они изолированы от побочных эффектов, которые имеют многие объекты. Карта - это чистый хеш, и нет никакой путаницы в попытках быть объектом одновременно. Карты также могут быть легко расширены с помощью функций прокси. У объекта в настоящее время есть класс Proxy, однако производительность и использование памяти мрачны, фактически создание собственного прокси-сервера, который выглядит так, как будто карта для объектов в настоящее время работает лучше, чем Proxy.
Существенным недостатком Карт является то, что они не поддерживаются с помощью JSON напрямую. Разбор возможен, но имеет несколько зависаний:
JSON.parse(str, (k,v) => {
if(typeof v !== 'object') return v;
let m = new Map();
for(k in v) m.set(k, v[k]);
return m;
});
Выше приведено серьезное снижение производительности, а также не поддерживаются строковые ключи. Кодирование JSON еще сложнее и проблематичнее (это один из многих подходов):
// An alternative to this it to use a replacer in JSON.stringify.
Map.prototype.toJSON = function() {
return JSON.stringify({
keys: Array.from(this.keys()),
values: Array.from(this.values())
});
};
Это не так плохо, если вы просто используете Карты, но у вас будут проблемы, когда вы смешиваете типы или используете нескалярные значения в качестве ключей (не то, чтобы JSON идеально подходил для такого рода проблем, как круговая ссылка на объект IE). Я не проверял это, но есть вероятность, что это сильно повредит производительности по сравнению с stringify.
У других языков сценариев часто нет таких проблем, поскольку они имеют явные нескалярные типы для Map, Object и Array. Веб-разработка часто представляет собой проблему с нескалярными типами, когда вам приходится иметь дело с такими вещами, как PHP объединяет массив / карту с объектом, используя A / M для свойств, а JS объединяет карту / объект с массивом, расширяющим M / O. Слияние сложных типов - проклятие дьявола для языков сценариев высокого уровня.
Пока это в значительной степени проблемы, связанные с внедрением, но важна и производительность для основных операций. Производительность также сложна, потому что это зависит от двигателя и использования. Пройдите мои тесты с долей соли, поскольку я не могу исключить любую ошибку (я должен спешить с этим). Вы также должны запустить свои собственные тесты, чтобы подтвердить, так как мои проверяют только очень конкретные простые сценарии, чтобы дать только приблизительное указание. Согласно тестам в Chrome для очень больших объектов / карт производительность для объектов хуже из-за удаления, которое, очевидно, каким-то образом пропорционально количеству ключей, а не O (1):
Object Set Took: 146
Object Update Took: 7
Object Get Took: 4
Object Delete Took: 8239
Map Set Took: 80
Map Update Took: 51
Map Get Took: 40
Map Delete Took: 2
Chrome явно имеет большое преимущество при получении и обновлении, но производительность удаления просто ужасна. Карты в этом случае используют чуть-чуть больше памяти (накладные расходы), но при проверке только одного объекта / карты с миллионами ключей влияние накладных расходов на карты выражается недостаточно хорошо. Объекты управления памятью также, кажется, освобождаются раньше, если я правильно читаю профиль, что может быть одним из преимуществ в пользу объектов.
В Firefox для этого конкретного теста это другая история:
Object Set Took: 435
Object Update Took: 126
Object Get Took: 50
Object Delete Took: 2
Map Set Took: 63
Map Update Took: 59
Map Get Took: 33
Map Delete Took: 1
Я должен сразу отметить, что в этом конкретном бенчмарке удаление из объектов в Firefox не вызывает никаких проблем, однако в других бенчмарках это вызывало проблемы, особенно когда имеется много ключей, как в Chrome. Карты явно превосходят Firefox для больших коллекций.
Однако это не конец истории, как насчет множества маленьких объектов или карт? Я сделал быстрый тест этого, но не исчерпывающий (настройка / получение), который лучше всего работает с небольшим количеством ключей в вышеуказанных операциях. Этот тест больше о памяти и инициализации.
Map Create: 69 // new Map
Object Create: 34 // {}
Опять же, эти цифры меняются, но в основном у Object хорошее преимущество. В некоторых случаях опережение «Объекты над картами» является экстремальным (~ в 10 раз лучше), но в среднем оно было в 2-3 раза лучше. Кажется, что экстремальные скачки производительности могут работать в обоих направлениях. Я только протестировал это в Chrome и создании, чтобы профилировать использование памяти и накладные расходы. Я был очень удивлен, увидев, что в Chrome оказалось, что Карты с одним ключом используют примерно в 30 раз больше памяти, чем Объекты с одним ключом.
Для тестирования множества мелких объектов со всеми вышеуказанными операциями (4 клавиши):
Chrome Object Took: 61
Chrome Map Took: 67
Firefox Object Took: 54
Firefox Map Took: 139
С точки зрения распределения памяти они вели себя одинаково с точки зрения освобождения / GC, но Map использовала в 5 раз больше памяти. В этом тесте использовалось 4 ключа, где, как и в последнем тесте, я установил только один ключ, так что это объясняет сокращение накладных расходов памяти. Я провел этот тест несколько раз, и карта / объект в целом более или менее похожи на Chrome с точки зрения общей скорости. В Firefox для небольших объектов есть определенное преимущество в производительности по сравнению с картами в целом.
Это, конечно, не включает в себя отдельные варианты, которые могут сильно отличаться. Я бы не советовал микрооптимизировать с этими цифрами. Что из этого можно сделать, так это то, что, как правило, более тщательно рассмотрите Карты для хранилищ с очень большими значениями ключей и объекты для хранилищ с небольшими значениями ключей.
Кроме того, лучшая стратегия с этими двумя - это реализовать ее и просто заставить работать в первую очередь. При профилировании важно иметь в виду, что иногда вещи, о которых вы не думаете, будут медленными, если смотреть на них, они могут быть невероятно медленными из-за причуд движка, как это видно в случае удаления ключа объекта.