这篇文章将为大家详细讲解有关如何使用JavaScript的Map提升性能,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。
Map与常规对象有什么不同
Map和常规对象主要有2个不同之处。
1.无限制的键(Key)
常规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.直接遍历
在常规对象中,为了遍历keys、values和entries,你必须将它们转换为数组,如使用Object.keys()、Object.values()和Object.entries(),或者使用for ... in循环,因为常规对象不能直接遍历,另外for ... in循环还有一些限制:它仅仅遍历可枚举属性、非Symbol属性,并且遍历的顺序是任意的。
而Map可以直接遍历,并且由于它是键控集合,遍历的顺序和插入键值的顺序是一致的。你可以使用for ... of循环或forEach方法来遍历Map的entries,如下代码:
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通过键值(key/value)成对出现,就像你可以把一个数组转换为Set,你也可以把二维数组转换为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转换map为object之前,确保map的key在转换为字符串时会产生唯一的结果,否则你将面临数据丢失的风险。
性能测试
为了准备测试,我会创建一个对象和一个map,它们都有1000000个相同的键值。
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循环去创建常规对象和map。这个结果着实令人震惊,但是没有for循环,map添加属性的性能胜过常规对象。
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比常规对象更合适的例子,比如说我们想写一个函数去检查2个字符串是否由相同的字符串随机排序。
console.log(isAnagram('anagram', 'gramana')); // Should return true console.log(isAnagram('anagram', 'margnna')); // Should return false
有许多方法可以做到,但是这里,map可以帮忙我们创建一个最简单、最快速的解决方案:
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比object更合适。
关于“如何使用JavaScript的Map提升性能”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。