红宝书学习笔记(2)JavaScript里面的Map和WeakMap类型

1. Map

通常我们是使用对象来表示键值对关系,如下:

let obj = {
	name: '李四',
	age: 18,
	desc: '一个憨批'
};

对于大部分情况,使用对象已经足够了。但是对象的键名只能使用字符串,其余类型会被转为字符串。

let student1 = {
	name: '李四',
	age: 18
};
let student2 = {
	name: '李四',
	age:19
};
let score = {
	[student1]: 79,
	[student2]: 89
};
// {[object Object]: 89}

如上,对象被转为字符串,所以score中只有一个元素,前面的student1被后面的覆盖了。为了解决这种情况,我们可以使用Map

let student1 = {
	name: '李四',
	age: 18
};
let student2 = {
	name: '李四',
	age:19
};
let score = new Map([
	[student1, 79],
	[student2, 89]
]);

此时score如下:
在这里插入图片描述
这时我们发现两个学生的成绩被分开了。这是因为Map的键名可以是任意类型的值,JavaScript内部使用SameValueZero比较操作来区分不同的键。基本相当于使用严格对象相等的标准来检测。接下来介绍Map的语法。
Map和伪数组类似,存在一个属性用来表示其长度。

let map = new Map([
	['元素1', 1],
	['元素2', 2]
]);
map.size; // 2

Map使用set添加元素,使用get获取元素,使用delete删除元素,使用has判断Map中是否存在某个键值对,使用clear清空Map

let map = new Map();
map.set('元素1', 70);
map.get('元素1'); // 70
map.delete('元素1'); // true
map.set('元素2', 80);
map.has('元素2'); // true
map.has('元素3'); // false
map.clear(); // 此时map为空

Map原型上有keys()values()entries()方法,他们返回一个迭代器。因此可以使用for...of...方法进行遍历。

let map = new Map([
	['元素1', 'one'],
	['元素2', 'two'],
	['元素3', 'three']
]);
for (const key of map.keys()) {
	console.log(key);
}
// 元素1
// 元素2
// 元素3
for (const value of map.values()) {
	console.log(value);
}
// one
// two
// three
for (const item of map.entries()) {
	console.log(item);
}
// ['元素1','one']
// ['元素2','two']
// ['元素3','three']

由于Symbol.iterator引用entries,因此还可以这么使用:

for (const item of map[Symbol.iterator]()) {
	console.log(item);
}
map.entries === map[Symbol.iterator]
// ['元素1','one']
// ['元素2','two']
// ['元素3','three']
// true

结合解构,可以这么用:

for (const [key, value] of map.entries()) {
	console.log(`key: ${key} <-----> value: ${value}`);
}
// key: 元素1 <-----> value: one
// key: 元素2 <-----> value: two
// key: 元素3 <-----> value: three

因为entries是默认迭代器,因此可以直接这么写:

for (const [key, value] of map) {
	console.log(`key: ${key} <-----> value: ${value}`);
}
// key: 元素1 <-----> value: one
// key: 元素2 <-----> value: two
// key: 元素3 <-----> value: three

也可以解构Map

[...map]
// [["元素1", "one"]
// ["元素2", "two"]
// ["元素3", "three"]]

也可以使用forEach遍历,用法和数组一样

map.forEach((value, key)=> {
	console.log(value, key);
});
// one 元素1
// two 元素2
// three 元素3

可以使用二维数组初始化Map。数组元素第一项为键,第二项为值。

new Map([
	['one', 1],
	['two', 2]
])

如果键名为对象,那么即使修改了对象属性,也不会影响其在Map映射中的位置。

let map = new Map();
let obj = { a: 1 };
map.set(obj, '值');
obj.a = 2;
map.get(obj); // 值

需要注意的事,在Map中,如果两个键名都为NaN,那么Map中只会存储一个,后一个会覆盖前一个的值。

let map = new Map([
	[NaN, 1],
	[NaN, 2]
]);
map.forEach((value, key) => {
	console.log(value, key);
});
// 2 NaN

MapObject的区别

  • Object是无序的,Map循环时会有序输出
  • 给定内存的情况下,使用Map可以比Object多存储大约50%的键值对
  • MapObject的插入性能差不多,但是涉及大量插入操作时Map性能更佳。
  • MapObject的查找性能差不多,但是如果数据量小则Object更快,另外在将Object键名为连续整数时,浏览器可以进行优化,此时Object性能更好。
  • Object的删除操作不推荐使用delete(性能不好),但是可以放心的使用delete删除Map中的元素,且在Map中删除的性能比查找和插入更好。因此大量删除操作使用Map更好。

2. WeakMap

WeakMapMap最大的区别在于垃圾回收机制对其的态度。在Map中,即使作为键名的对象被解除引用了,在Map中仍然存在这个值:

let map = new Map();
let obj = {
    o: { a: 1 }
};
map.set(obj.o, 'obj');
obj.o = null;
console.log(map);

在这里插入图片描述
WeakMap弱映射,它的键不会计入垃圾回收机制。也就是说,一旦键不再被引用了,则垃圾回收机制会将它回收掉。

let wm = new WeakMap();
let obj = {
	o: {
		a: '3'
	}
};
wm.set(obj.o, '测试');
console.log(wm);

在这里插入图片描述

// 删除 obj.o 的引用
obj.o = null;
console.log(wm);

在这里插入图片描述
我们发现WeakMap里面已经清空了。因此,WeakMap的长度是不确定的,所以他没有size属性,也不能遍历,没有clear()方法。WeakMapset()get()has()delete()方法。且WeakMap里面的键只能为对象类型的,这是因为如果使用基本数据类型,则无法判断初始化时使用的键名和初始化后使用的是否是同一个键名。
WeakMapAPIMap的子集。因此使用方法和Map相同。

let wm = new WeakMap([])
	obj = {};
wm.set(obj, 1);
wm.get(obj); // 1
wm.has(obj); // true
wm.delete(obj); // true

WeakMap的一个应用是用于实现私有变量。

const User = (function () {
	const wm = new WeakMap();
	class User {
		constructor(id) {
			this.idProperty = Symbol(id);
			this.setId(id);
		}
		setId(id) {
			this.setPrivate(this.idProperty, id);
		}
		getId() {
			return this.getPrivate(this.idProperty);
		}
		setPrivate(property, id) {
			const privateMembers = wm.get(this) || {};
			privateMembers[property] = id;
			wm.set(this, privateMembers);
		}
		getPrivate(property) {
			return wm[this][property];
		}
	}
	return User;
})();

WeakMap还可以用于存储DOM节点。这样在删除DOM节点后则WeakMap里面存储的键值对也会被垃圾回收机制回收。

const li = document.getElementsByTagName[0],
	wm = new WeakMap();
wm.set(li, li.innerHTML);
document.removeChild(li);

如上,如果li节点被删除了,则wm中的键值对也被清空了。这样不需要手动清空,避免了内存浪费。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值