Среди новых функций, представленных в JavaScript в ES6, мы видимSetа такжеMapвведение. с обычными предметами иArrayРазница в том, что они являются "ключевыми коллекциями". Тем не менее, они ведут себя немного по-разному, и при использовании в определенных контекстах они могут обеспечить значительные преимущества в производительности.
В этой статье я разберуMap, чем именно он отличается, где может пригодиться и какие преимущества в производительности перед обычными объектами.
MapЧем он отличается от обычных объектов
MapЕсть два основных отличия от обычных объектов.
1. Неограниченное количество ключей (Ключ)
Ключи обычных объектов JavaScript должны бытьStringилиSymbol, следующий объект иллюстрирует это:
const symbol = Symbol();
const string2 = 'string2';
const regularObject = {
string1: 'value1',
[string2]: 'value2',
[symbol]: 'value3'
};
Напротив,MapПозволяет использовать функции, объекты и другие простые типы (включая NaN) в качестве ключей, например:
const func = () => null;
const object = {};
const array = [];
const bool = false;
const map = new Map();
map.set(func, 'value1');
map.set(object, 'value2');
map.set(array, 'value3');
map.set(bool, 'value4');
map.set(NaN, 'value5');
Эта функция обеспечивает большую гибкость при связывании различных типов данных.
2. Прямой обход
В обычных объектах, чтобы перебирать ключи, значения и записи, вы должны преобразовать их в массивы, например, используяObject.keys(),Object.values()а такжеObject.entries()или используйтеfor ... inцикл, потому что обычные объекты не могут быть пройдены напрямую, и, кроме того,for ... inЦикл также имеет некоторые ограничения: он проходит только по перечислимым свойствам, свойствам, не относящимся к символам, и порядок обхода произвольный.
а такжеMapЕго можно пройти напрямую, и, поскольку это коллекция с ключом, порядок обхода согласуется с порядком, в котором вставляются ключевые значения. вы можете использоватьfor ... ofпетля илиforEachметод прохожденияMapзаписи, следующий код:
for (let [key, value] of map) {
console.log(key);
console.log(value);
};
map.forEach((key, value) => {
console.log(key);
console.log(value);
});
Еще одним преимуществом является то, что вы можете позвонитьmap.sizeсвойство, чтобы получить количество ключей, а для обычных объектов, чтобы сделать это, вам нужно сначала преобразовать в массив, а затем получить длину массива, например:Object.keys({}).length.
Mapа такжеSetВ чем разница
Mapповедение иSetОчень похожи, и все они содержат одни и те же методы, в том числе: has, get, set, delete. Обе они являются коллекциями с ключами, что означает, что вы можете использовать что-то вродеforEachметод обхода элементов, порядок упорядочивается в соответствии со значением ключа вставки.
Самая большая разница в том,MapПо парам ключ/значение, например, вы можете преобразовать массив вSet, вы также можете преобразовать 2D-массив вMap:
const set = new Set([1, 2, 3, 4]);
const map = new Map([['one', 1], ['two', 2], ['three', 3], ['four', 4]]);
преобразование типов
кMapПереключение к массиву, вы можете использовать синтаксическую структуру для ES6:
const map = new Map([['one', 1], ['two', 2]]);
const arr = [...map];
До сих порMapПреобразование в обычные объекты и обратно по-прежнему не очень удобно, поэтому вам может понадобиться использовать метод функции, как показано ниже:
const mapToObj = map => {
const obj = {};
map.forEach((key, value) => { obj[key] = value });
return obj;
};
const objToMap = obj => {
const map = new Map();
Object.keys(obj).forEach(key => { map.set(key, obj[key]) });
return map;
};
Но теперь, на дебюте ES2019 в августе, мы увиделиObjectВведено 2 новых метода:Object.entries()а такжеObject.fromEntries(), что значительно упрощает описанный выше метод:
const obj2 = Object.fromEntries(map);
const map2 = new Map(Object.entries(obj));
когда вы используетеObject.fromEntriesПеред преобразованием карты в объект убедитесь, что ключ карты будет давать уникальный результат при преобразовании в строку, иначе вы рискуете потерять данные.
Тестирование производительности
Чтобы подготовиться к тесту, я создам объект и карту, каждая из которых имеет 1 000 000 одинаковых значений ключа.
let obj = {}, map = new Map(), n = 1000000;
for (let i = 0; i < n; i++) {
obj[i] = i;
map.set(i, i);
}
тогда я используюconsole.time()Чтобы измерить тест, точность времени может колебаться из-за моей конкретной системы и версии Node.js. Результаты испытаний демонстрируют использованиеMapприрост производительности, особенно при добавлении и удалении ключей.
Запрос
let result;
console.time('Object');
result = obj.hasOwnProperty('999999');
console.timeEnd('Object');
// Object: 0.250ms
console.time('Map');
result = map.has(999999);
console.timeEnd('Map');
// Map: 0.095ms (2.6 times faster)
Добавить к
console.time('Object');
obj[n] = n;
console.timeEnd('Object');
// Object: 0.229ms
console.time('Map');
map.set(n, n);
console.timeEnd('Map');
// Map: 0.005ms (45.8 times faster!)
удалять
console.time('Object');
delete obj[n];
console.timeEnd('Object');
// Object: 0.376ms
console.time('Map');
map.delete(n);
console.timeEnd('Map');
// Map: 0.012ms (31 times faster!)
Mapпри каких обстоятельствах медленнее
При тестировании я обнаружил одну ситуацию, когда обычные объекты работают лучше: использованиеforЦикл для создания обычных объектов и карт. Этот результат действительно шокирует, но нет.forЗацикливание, добавление свойств карты превосходит обычные объекты.
console.time('Object');
for (let i = 0; i < n; i++) {
obj[i] = i;
}
console.timeEnd('Object');
// Object: 32.143ms
let obj = {}, map = new Map(), n = 1000000;
console.time('Map');
for (let i = 0; i < n; i++) {
map.set(i, i);
}
console.timeEnd('Map');
// Map: 163.828ms (5 times slower)
Например
Наконец, давайте посмотрим наMapБолее подходящий пример, чем обычные объекты, скажем, мы хотим написать функцию для проверки того, упорядочены ли две строки случайным образом по одной и той же строке.
console.log(isAnagram('anagram', 'gramana')); // Should return true
console.log(isAnagram('anagram', 'margnna')); // Should return false
Есть много способов сделать это, но здесь карта может помочь нам создать одно из самых простых и быстрых решений:
const isAnagram = (str1, str2) => {
if (str1.length !== str2.length) {
return false;
}
const map = new Map();
for (let char of str1) {
const count = map.has(char) ? map.get(char) + 1 : 1;
map.set(char, count);
}
for (let char of str2) {
if (!map.has(char)) {
return false;
}
const count = map.get(char) - 1;
if (count === 0) {
map.delete(char);
continue;
}
map.set(char, count);
}
return map.size === 0;
};
В этом примере карта больше подходит, чем объект, когда речь идет о динамическом добавлении и удалении ключей, а структура данных (или количество ключей) не может быть определена заранее.
Я надеюсь, что эта статья поможет вам, если вы еще не использовали ееMap, также может расширить ваш кругозор и измерить ценность современного JavaScript.
Примечание переводчика: Лично я не согласен с точкой зрения автора.Из вышеприведенного описания Карта больше похожа на затраты места в обмен на прибавку в скорости. Тогда для измерения пространства и скорости должен быть порог. Когда количество данных относительно невелико, по сравнению с увеличением скорости, стоимость жертвуемого места больше.В это время очевидно, что использование Map не подходит, когда количество данных достаточно велико, влияние стоимость места меньше в это время. Итак, посмотрите, как разработчики измеряют взаимосвязь между ними и выбирают оптимальное решение.