目录
JavaScript 的对象(Object),本质上是键值对的集合(Hash 结构),但是传统上只能用字符串当作键(ES6加入了Symbol作为属性名称)
Map
Object对象与Map结构对象
- Object 结构提供了“字符串—值”的对应;
- Map 结构提供了“值—值”的对应,Map是“键值对”的数据结构,Map键的”范围不限于字符串,各种类型的值(包括对象)都可以当作键。
创建Map结构对象
new Map() 括号中的参数必须是在一个双数组中以键值对存放的数组对象,如[[key1,value1],[key2,value2],...[keyn,valuen]]
let set = new Set([['及时雨','宋公明'],['玉麒麟','卢俊义']]);
任何具有 Iterator 接口且每个成员都是一个双元素的数组( [[a,b],[c,d] ] )的数据结构都可以当作Map构造函数的参数。这就是说,Set和Map都可以用来生成新的 Map。
let map = new Map([['2','er'],['3','san']]); let set = new Set([['及时雨','宋公明'],['玉麒麟','卢俊义']]); let a1 = new Map(map); let a2 = new Map(set); console.log(a1); console.log(a2);
Map数据结构的属性和方法
增删改查
- size属性:返回 Map 结构的成员(一个键值对算一个)总数
- set(key, value)方法:设置键名key对应的键值为value,然后返回整个 Map 结构。如果key已经有值,则键值会被更新,否则就新生成该键(如果对同一个键多次赋值,后面的值将覆盖前面的值)。
let bx = document.getElementById('box'); let arr2 = { bx: 'obj对象' }; mp.set(arr2, '盒子对象'); // 使用Map对象的set方法添加数据,Map对象的键可以是obj对象 mp.set(mp_num, 'map对象');
- get(key)方法:读取key对应的键值,如果找不到key,返回undefined
let arr1 = ['one', '一']; let mp_num = new Map([arr1]); // 创建一个有键值对的Map对象 console.log(mp_num.get('one')); // 获得Map对象键所对应的键值
- has(key)方法:返回一个布尔值,表示某个键是否在当前 Map 对象之中。
- delete(key)方法:删除某个键,返回true。如果删除失败,返回false。
console.log(mp_num.delete('on')); // 返回false console.log(mp_num.delete('one')); // 返回true console.log(mp_num.get('one')); // 返回undefined
- clear()方法:清除所有成员,没有返回值。
遍历
- keys():返回键名的遍历器
- values():返回键值的遍历器
- entries():返回所有成员的遍历器
- forEach():遍历 Map 的所有成员
let set = new Set([['及时雨', '宋公明'], ['玉麒麟', '卢俊义']]); let a2 = new Map(set); for (let key of a2.keys()) { // keys方法遍历键 console.log(key); }; for (let value of a2.values()) { // values方法遍历值 console.log(value); }; for (let [key, value] of a2.entries()) { // entries()遍历键值对 console.log(key, value); }; for (let iet of a2.entries()) { // entries()遍历键值对 console.log(iet); }; for (let [key, value] of a2) { // 遍历键值对 console.log(key, value); }; a2.forEach(function (key, value, a2) { // forEach方法遍历 console.log('键名key:' + key, '键值value:' + value); });
特别注意:Map 的遍历顺序就是插入顺序
Map的forEach方法
forEach方法还可以接受第二个参数,用来绑定this。 上面代码中,forEach方法的回调函数的this,就指向reporter
Map与数组之间的转换
Map转为数组
使用扩展运算符...
let c = new Map() .set('神算子', '蒋敬') .set('神行太保', '戴宗'); let arr_c = [...c]; console.log(arr_c);
如果所有 Map 的键都是字符串,它可以无损地转为对象。如果有非字符串的键名,那么这个键名会被转成字符串,再作为对象的键名
扩展运算符(...)
Map结构转为数组结构,比较快速的方法是使用扩展运算符(...),可以根据需求选择对值还是键名或是键值对进行数组转换
结合数组的map方法、filter方法,可以实现 Map 的遍历和过滤(Map 本身没有map和filter方法)
数组转为Map
使用创建Map对象的方法将数组转为Map
WeakMap
WeakMap的专用场合
- 它的键所对应的对象,可能会在将来消失
- WeakMap结构有助于防止内存泄漏
WeakMap结构与Map结构异同
相同点:
- 都是用于生成键值对的集合
- 四种方法都可用:get()、set()、has()、delete()
不同点:
- WeakMap只接受对象作为键名(null除外),不接受其他类型的值作为键名;
<body> <div id="box1">盒子1</div> <div id="box2">盒子2</div> <div id="box3">盒子3</div> <script> let b1 = document.getElementById('box1'); let b2 = document.getElementById('box2'); let b3 = document.getElementById('box3'); let wmp = new WeakMap(); wmp.set(b1,b1.innerText); </script> </body>
- WeakMap的键名所指向的对象,不计入垃圾回收机制(不算是引用)
- 没有遍历操作(即没有keys()、values()和entries()方法),也没有size属性。因为没有办法列出所有键名,某个键名是否存在完全不可预测,跟垃圾回收机制是否运行相关。这一刻可以取到键名,下一刻垃圾回收机制突然运行了,这个键名就没了,为了防止出现不确定性,就统一规定不能取到键名
- 无法清空,即不支持clear方法
经典应用示例
1、myElement是一个 DOM 节点,每当发生click事件,就更新一下状态。我们将这个状态作为键值放在 WeakMap 里,对应的键名就是myElement。一旦这个 DOM 节点删除,该状态就会自动消失,不存在内存泄漏风险
2、部署私有属性。Countdown类的两个内部属性_counter和_action,是实例的弱引用,所以如果删除实例,它们也就随之消失,不会造成内存泄漏。