Set WeakSet和Map WeakMap
Set WeakSet
简介
Set:Set是JavaScript的一个内置对象,是一种数据结构。Set对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用。
Set:
(1)成员不能重复,常用来做数组去重;
(2)Set集合只有键值,没有键名,有点像数组;
(3)可以遍历,有add、delete、has等方法,有size属性;
(4)NaN和undefined都可以被存储在 Set 中,NaN之间被视为相同的值(尽管 NaN !== NaN),因此NaN在Set集合中最多只能出现一次。
WeakSet:WeakSet也是JavaScript的一个内置对象,是一种数据结构,WeakSet对象允许你将弱保持对象存储在一个集合中。
WeakSet:
(1)WeakSet只能是对象的集合,添加字符串,数字,undefined,null等基本数据类型都会报错;
(2)WeakSet只有键值,没有键名,键值是可以重复的,有点像一个对象数组;
(3)没有遍历的办法,有add、delete、has等方法,没有size属性;
(4)WeakSet实例a中添加了对象{a:1},调用a.has({a:1})为false,也无法通过a.delete({a:1})删除。应该先声明const obj={a:1},a.has(obj)为true,变量obj指向堆内存中的{a:1},具有唯一标识作用。
- 注意
WeakSet中的对象都是弱引用:即垃圾回收机制不考虑WeakSet对其内部对象的引用,也就是说,在WeakSet中的对象当没有外界其他对象引用它时,会被垃圾回收机制回收,并不考虑WeakSet中还有它。
- 通俗点说就是,假如WeakSet里面有对象a,而对象a此时外部环境并没有引用它,它唯一被引用的地方就是它存在于WeakSet内,那么垃圾回收机制就会毫不留情,直接一把带走。
- 因为当外部环境没有引用a时,a就会被清走,所以可以理解为WeakSet的大小和顺序不稳定,所以不能被遍历,也没有size属性。
- WeakSet成员都是弱引用,随时可以消失。可以用来保存 DOM 节点,不容易造成内存泄露;
Map WeakMap
简介
Map:Map是JavaScript的一个内置对象,是一种数据结构。Map对象保存键值对,并且能够记住键的原始插入顺序。任何值(对象或者基本类型)都可以作为一个键或一个值。
Map:
(1)一个键名只能出现一次;
(2)有set、get、has、delete、clear等增删改查方法,有size属性;
(3)与Set系列不同,它有keys、values、entries等方法;(见下)
(4)可以使用for of、forEach遍历;
(5)可以与其他数据结构互相转换。(见下)
// 第(3)点
let map1 = new Map();
map1.set([1, 2], { a: 1 });
map1.set("a", [3, 4]);
console.log(map1.keys());
console.log(map1.values());
console.log(map1.entries());
- keys():返回一个新的迭代对象,其中包含
Map对象中所有的键,并以插入Map对象的顺序排列。
- values():返回一个新的迭代对象,其中包含
Map对象中所有的值,并以插入Map对象的顺序排列。
- entries():返回一个新的迭代对象,其为一个包含
Map对象中所有键值对的[key, value]数组,并以插入Map对象的顺序排列。
第(5)点:Map和其他数据结构的转换
- Map和数组
// Map转数组
const map = new Map();
map.set("user", "小王");
const arr = [...map];
// 数组转Map
// 数组的第一维的维数表示转成后Map的键值对个数
// 每一个数组的前两项,第一项为键,第二项为值,多余的项舍去
new Map([
[2, [9, 10], 4, 5], // 2 => [9,10]
[1, 4, 5], // 1 => 4
[{ a: 1 }, 7], // { a : 1 } => 7
]);
- Map和对象
// 键值为字符串的map可以转成对象
const map=new Map();
map.set("user", "小王");
map.set("joiner", "小龙");
Object.fromEntries(map.entries()) // {user:'小王',joiner:'小龙'}
// 对象转Map
// object.entries()可以先把对象转成可迭代的数组,然后再转Map
new Map(Object.entries({a:1,b:2})) // {'a' => 1,'b' => 2 }
补充:Map和对象的区别
1.Map默认情况不包含任何键,Object有原型,原型上可能有键值对;
2.Map的键可以是任何类型,Object的键只能是Symbol或String;
3.Map的大小直接用size获取,Object的键值个数只能迭代计算;
4.频繁增删改时使用Map更佳。
- Map和JSON
// Map转JSON
const map=new Map();
map.set("user", "小王");
map.set("joiner", "小龙");
Object.fromEntries(map.entries())
// JSON转Map
const json = {
a: "1",
b: "2",
};
new Map(Object.entries(json))
WeakMap:WeakMap是JavaScript的一个内置对象,是一种数据结构。WeakMap对象是一组键/值对的集合,其中的键是弱引用的。其键必须是对象,而值可以是任意的。
WeakMap:
(1)在weakMap中,{a:1}可以同时作为多个键值对的键名,值相同,地址不同;
(2)有set、get、has、delete、clear等增删改查方法,没有size属性,不能遍历;
(3)键名所指的对象不计入垃圾回收机制。
- 注意
- 因为 WeakMap 实例不会妨碍垃圾回收,所以非常适合保存关联元数据。
const m = new Map();
const btn = document.querySelector('#login');
// 给这个节点关联一些元数据
m.set(loginButton, {disabled: true});
假设在上面的代码执行后,页面被 JavaScript 改变了,原来的登录按钮从 DOM 树中被删掉了。但由于映射中还保存着按钮的引用,所以对应的 DOM 节点仍然会逗留在内存中,除非明确将其从映射中删除或者等到映射本身被销毁。如果这里使用的是弱映射,如以下代码所示,那么当节点从 DOM 树中被删除后,垃圾回收程序就可以立即释放其内存(假设没有其他地方引用这个对象):
const wm = new WeakMap();
const loginButton = document.querySelector('#login');
// 给这个节点关联一些元数据
wm.set(loginButton, {disabled: true});
- 相比于 WeakMap 实例,WeakSet 实例的用处没有那么大。不过,弱集合在给对象打标签时还是有价值的。
const disabledElements = new Set();
const loginButton = document.querySelector('#login');
// 通过加入对应集合,给这个节点打上“禁用”标签
disabledElements.add(loginButton);
以上通过查询元素在不在disabledElements中,就可以知道它是不是被禁用了。不过,假如元素从 DOM 树中被删除了,它的引用却仍然保存在 Set 中,因此即使元素已被删除,垃圾回收程序也不能回收它。为了让垃圾回收程序回收元素的内存,可以在这里使用 WeakSet:
const disabledElements = new WeakSet();
const loginButton = document.querySelector('#login');
// 通过加入对应集合,给这个节点打上“禁用”标签
disabledElements.add(loginButton);
这样,只要 WeakSet 中任何元素从 DOM 树中被删除,垃圾回收程序就可以忽略其存在,而立即释放其内存(假设没有其他地方引用这个对象)。所以WeakSet的经典作用:储存 DOM 节点,而不用担心这些节点从文档移除时引发的内存泄露。