JavaScript中Object、map、weakmap的区别分析


Posted in Javascript onDecember 15, 2020

前言

ECMAScript 6以前,在JavaScript中实现“键/值”式存储可以使用Object来方便高效地完成,也就是使用对象属性作为键,再使用属性来引用值。但这种实现并非没有问题,为此TC39委员会专门为“键/值”存储定义了一个规范。作为ECMAScript 6的新增特性,Map是一种新的集合类型,为这门语言带来了真正的键/值存储机制。Map的大多数特性都可以通过Object类型实现,但二者之间还是存在一些细微的差异。具体实践中使用哪一个,还是值得细细甄别。

一、map 的使用

初始化

object 可以使用字面量、构造函数、Object.crate的形式创建。而map 只能通过new 关键字和构造函数创建。对于map如果想在创建的同时初始化实例,可以给Map构造函数传入一个可迭代对象,需要包含键/值对数组。可迭代对象中的每个键/值对都会按照迭代顺序插入到新映射实例中:

object 的创建方式
 
const object = {}
const object1 = new Object()
const object2 = Object.create({})
 
map 的创建方式
//使用new关键字
const m0 = new Map;
// 使用嵌套数组初始化映射
const m1 = new Map([
 ["key1", "val1"],
 ["key2", "val2"],
 ["key3", "val3"]
]);
alert(m1.size); // 3
// 使用自定义迭代器初始化映射
const m2 = new Map({
[Symbol.iterator]: function*() {
 yield ["key1", "val1"];
 yield ["key2", "val2"];
 yield ["key3", "val3"];
}
});
alert(m2.size); // 3
// 映射期待的键/值对,无论是否提供
const m3 = new Map([[]]);
alert(m3.has(undefined)); // true
alert(m3.get(undefined)); // undefined

map键类型

与Object只能使用数值、字符串或符号作为键不同,Map可以使用任何JavaScript数据类型作为键。Map内部使用SameValueZero比较操作(ECMAScript规范内部定义,语言中不能使用),基本上相当于使用严格对象相等的标准来检查键的匹配性。与Object类似,映射的值是没有限制的。

const m = new Map();
 const functionKey = function() {};
 const symbolKey = Symbol();
 const objectKey = new Object();
 m.set(functionKey, "functionValue");
 m.set(symbolKey, "symbolValue");
 m.set(objectKey, "objectValue");
 alert(m.get(functionKey)); // functionValue
 alert(m.get(symbolKey)); // symbolValue
 alert(m.get(objectKey)); // objectValue
 // SameValueZero比较意味着独立实例不冲突
 alert(m.get(function() {})); // undefined

顺序与迭代

与Object类型的一个主要差异是,Map实例会维护键值对的插入顺序,因此可以根据插入顺序执行迭代操作。映射实例可以提供一个迭代器(Iterator),能以插入顺序生成[key,value]形式的数组。可以通过entries()方法(或者Symbol.iterator属性,它引用entries())取得这个迭代器:

const m = new Map([
 ["key1", "val1"],
 ["key2", "val2"],
 ["key3", "val3"]
]);
alert(m.entries === m[Symbol.iterator]); // true
for (let pair of m.entries()) {
 alert(pair);
}
// [key1,val1]
// [key2,val2]
// [key3,val3]
for (let pair of m[Symbol.iterator]()) {
 alert(pair);
}
// [key1,val1]
// [key2,val2]
// [key3,val3]

二、选择Object还是Map

对于多数Web开发任务来说,选择Object还是Map只是个人偏好问题,影响不 大。不过,对于在乎内存和性能的开发者来说,对象和映射之间确实存在显著的 差别。

1.内存占用

Object和Map的工程级实现在不同浏览器间存在明显差异,但存储单个键/值对所占用的内存数量都会随键的数量线性增加。批量添加或删除键/值对则取决于各浏览器对该类型内存分配的工程实现。不同浏览器的情况不同,但给定固定大小的内存,Map大约可以比Object多存储50%的键/值对。

2.插入性能

向Object和Map中插入新键/值对的消耗大致相当,不过插入Map在所有浏览器中一般会稍微快一点儿。对这两个类型来说,插入速度并不会随着键/值对数量而线性增加。如果代码涉及大量插入操作,那么显然Map的性能更佳。

3.查找速度

与插入不同,从大型Object和Map中查找键/值对的性能差异极小,但如果只包含少量键/值对,则Object有时候速度更快。在把Object当成数组使用的情况下(比如使用连续整数作为属性),浏览器引擎可以进行优化,在内存中使用更高效的布局。这对Map来说是不可能的。对这两个类型而言,查找速度不会随着键/值对数量增加而线性增加。如果代码涉及大量查找操作,那么某些情况下可能选择Object更好一些。

4.删除性能

使用delete删除Object属性的性能一直以来饱受诟病,目前在很多浏览器中仍然如此。为此,出现了一些伪删除对象属性的操作,包括把属性值设置为undefined或null。但很多时候,这都是一种讨厌的或不适宜的折中。而对大多数浏览器引擎来说,Map的delete()操作都比插入和查找更快。如果代码涉及大量删除操作,那么毫无疑问应该选择Map

三、weakMap

ECMAScript 6新增的“弱映射”(WeakMap)是一种新的集合类型,为这门语言带来了增强的键/值对存储机制。WeakMap是Map的“兄弟”类型,其API也是Map的子集。WeakMap中的“weak”(弱),描述的是JavaScript垃圾回收程序对待“弱映射”中键的方式。

weakcMap 的弱
WeakMap中“weak”表示弱映射的键是“弱弱地拿着”的。意思就是,这些键不属于正式的引用,不会阻止垃圾回收,当浏览器需要回收内存时这些键是可能会被回收的。但要注意的是,弱映射中值的引用可不是“弱弱地拿着”的。只要键存在,键/值对就会存在于映射中,并被当作对值的引用,因此就不会被当作垃圾回收。

//会被回收
const wm = new WeakMap();
wm.set({}, "val");

//不会被回收
const wm2 = new WeakMap();
const container = {
 key: {}
};
wm2.set(container.key, "val");
function removeReference() {
 container.key = null;
}

在vm中,set()方法初始化了一个新对象并将它用作一个字符串的键。因为没有指向这个对象的其他引用,所以当这行代码执行完成后,这个对象键就会被当作垃圾回收。然后,这个键/值对就从弱映射中消失了,使其成为一个空映射。在这个例子中,因为值也没有被引用,所以这对键/值被破坏以后,值本身也会成为垃圾回收的目标。
而在vm1中,container对象维护着一个对弱映射键的引用,因此这个对象键不会成为垃圾回收的目标。不过,如果调用了removeReference(),就会摧毁键对象的最后一个引用,垃圾回收程序就可以把这个键/值对清理掉。

weakMap使用
WeakMap的初始化与map并没有什么太大的差别,需要注意的是weakMap只能使用object类型的键,这与weakMap的作用是息息相关的

const key1 = {id: 1},
 key2 = {id: 2},
 key3 = {id: 3};
// 使用嵌套数组初始化弱映射
const wm1 = new WeakMap([
 [key1, "val1"],
 [key2, "val2"],
 [key3, "val3"]
]);
alert(wm.get(key1)); // val1
alert(wm.get(key2)); // val2
alert(wm.get(key3)); // val3

// 原始值可以先包装成对象再用作键
const stringKey = new String("key1");
const wm3 = new WeakMap([
 stringKey, "val1"
]);
alert(wm3.get(stringKey)); // "val1"

四、使用weakMap的场景

WeakMap实例与现有JavaScript对象有着很大不同,可能一时不容易说清楚应该怎么使用它。这个问题没有唯一的答案,但已经出现了很多相关策略。

DOM节点元数据
因为WeakMap实例不会妨碍垃圾回收,所以非常适合保存关联元数据。如以下代码所示,下面的例子使用的是WeakMap,当节点从DOM树中被删除后,垃圾回收程序就可以立即释放其内存(假设没有其他地方引用这个对象):

const wm = new WeakMap();
const loginButton = document.querySelector('#login');
// 给这个节点关联一些元数据
wm.set(loginButton, {disabled: true});

总结

以上就是object、map、weakmap的相关使用和区别了。其实在大多数情况下,object和map使用是没有什么区别的,但是如果你需要大量的插入和查找删除,或者需要使用对象作为键值的话,使用map是比较优的选择。另外weakMap在使用的对象可能会被动态删除的情况下,比map具有优化内存的优势。

以上就是JavaScript中Object、map、weakmap的区别分析的详细内容,更多关于JavaScript中Object、map、weakmap区别的资料请关注三水点靠木其它相关文章!

Javascript 相关文章推荐
让iframe框架网页在任何浏览器下自动伸缩
Aug 18 Javascript
jQuery选择器中含有空格的使用示例及注意事项
Aug 25 Javascript
jQuery中insertAfter()方法用法实例
Jan 08 Javascript
JavaScript编程中的Promise使用大全
Jul 28 Javascript
javascript表单处理具体实现代码(表单、链接、按钮)
May 07 Javascript
Bootstrap 附加导航(Affix)插件实例详解
Jun 01 Javascript
vue环形进度条组件实例应用
Oct 10 Javascript
JavaScript数组去重的方法总结【12种方法,号称史上最全】
Feb 28 Javascript
vue中v-text / v-html使用实例代码详解
Apr 02 Javascript
小程序实现搜索界面 小程序实现推荐搜索列表效果
May 18 Javascript
vue 父组件中调用子组件函数的方法
Jun 06 Javascript
webpack3升级到webpack4遇到问题总结
Sep 30 Javascript
JavaScript中遍历的十种方法总结
Dec 15 #Javascript
token 机制和实现方式
Dec 15 #Javascript
vue从后台渲染文章列表以及根据id跳转文章详情详解
Dec 14 #Vue.js
一分钟学会JavaScript中的try-catch
Dec 14 #Javascript
Vue在H5 项目中使用融云进行实时个人单聊通讯
Dec 14 #Vue.js
vue的hash值原理也是table切换实例代码
Dec 14 #Vue.js
element-ui点击查看大图的方法示例
Dec 14 #Javascript
You might like
实用的简单PHP分页集合包括使用方法
2013/10/21 PHP
php mail to 配置详解
2014/01/16 PHP
如何阻止网站被恶意反向代理访问(防网站镜像)
2014/03/18 PHP
PHP中对各种加密算法、Hash算法的速度测试对比代码
2014/07/08 PHP
js打印纸函数代码(递归)
2010/06/18 Javascript
jquery监控数据是否变化(修正版)
2011/04/12 Javascript
提交表单时执行func方法实现代码
2013/03/17 Javascript
容易造成JavaScript内存泄露几个方面
2014/09/04 Javascript
JavaScript中getUTCMinutes()方法的使用详解
2015/06/10 Javascript
js实现的黑背景灰色二级导航菜单效果代码
2015/08/24 Javascript
JavaScript 数组中最大最小值
2016/06/05 Javascript
AngularJS控制器详解及示例代码
2016/08/16 Javascript
JavaScript使用键盘输入控制实现数字验证功能
2016/08/19 Javascript
JSONP跨域请求
2017/03/02 Javascript
微信禁止下拉查看URL的处理方法
2017/09/28 Javascript
jquery使用iscorll实现上拉、下拉加载刷新
2017/10/26 jQuery
bootstrap table表格插件之服务器端分页实例代码
2018/09/12 Javascript
如何在vue里面优雅的解决跨域(路由冲突问题)
2019/01/20 Javascript
javascript中数组的常用算法深入分析
2019/03/12 Javascript
WebGL three.js学习笔记之阴影与实现物体的动画效果
2019/04/25 Javascript
详解Vue+ElementUI从零开始搭建自己的网站(一、环境搭建)
2019/04/30 Javascript
no-vnc和node.js实现web远程桌面的完整步骤
2019/08/11 Javascript
vue数据更新UI不刷新显示的解决办法
2020/08/06 Javascript
[03:06]V社市场总监Dota2项目负责人Erik专访:希望更多中国玩家加入DOTA2
2014/07/11 DOTA
Python回文字符串及回文数字判定功能示例
2018/03/20 Python
python实现对象列表根据某个属性排序的方法详解
2019/06/11 Python
Python字典中的值为列表或字典的构造实例
2019/12/16 Python
Python将二维列表list的数据输出(TXT,Excel)
2020/04/23 Python
美国隐形眼镜网:Major Lens
2018/02/09 全球购物
光声世纪笔试题目
2012/08/25 面试题
捐书寄语赠言
2014/01/18 职场文书
发展部经理职责规定
2014/02/22 职场文书
中秋节主持词
2014/04/02 职场文书
洗手间标语
2014/06/23 职场文书
2019年幼儿园管理条例范本!
2019/07/17 职场文书
Python内置包对JSON文件数据进行编码和解码
2022/04/12 Python