WeakSet
WeakSet的基本使用
- 和Set类似的另外一个数据结构称之为WeakSet,也是内部元素不能重复的数据结构。
- 那么和Set有什么区别呢?
- 区别一:WeakSet中只能存放对象类型,不能存放基本数据类型;
- 区别二:WeakSet对元素的引用是弱引用,如果没有其他引用对某个对象进行引用,那么垃圾回收机制可以对该对象进行回收;
代码演示
const weakSet = new WeakSet() // 1.区别一: 只能存放对象类型 // TypeError: Invalid value used in weak set // weakSet.add(10) // 强引用和弱引用的概念(看图) // 2.区别二: 对对象是一个弱引用 let obj = { name: "why" } weakSet.add(obj) const set = new Set() // 建立的是强引用 set.add(obj) // 建立的是弱引用 weakSet.add(obj)
WeakSet常见方法
- add(value):添加某个元素,返回WeakSet对象本身;
- delete(value):从WeakSet中删除和这个值相等的元素,返回boolean类型;
- has(value):判断WeakSet中是否存在某个元素,返回boolean类型;
WeakSet的应用
- 注意:WeakSet不能遍历
- 因为WeakSet只是对对象的弱引用,如果我们遍历获取到其中的元素,那么有可能造成对象不能正常的销毁。
- 所以存储到WeakSet中的对象是没办法获取的;
- 那么这个东西有什么用呢?
- 事实上这个问题并不好回答,我们来使用一个Stack Overflow上的答案;
代码演示
不能通过非构造方法创建出来的对象 调用构造函数的方法
const personSet = new WeakSet() class Person { constructor() { personSet.add(this) } running() { if (!personSet.has(this)) { throw new Error("不能通过非构造方法创建出来的对象调用running方法") } console.log("running~", this) } } let p = new Person() p.running() p = null p.running.call({name: "why"})
Map
Map的基本使用
- 另外一个新增的数据结构是Map,用于存储映射关系。
- 但是我们可能会想,在之前我们可以使用对象来存储映射关系,他们有什么区别呢?
- 事实上我们对象存储映射关系只能用字符串(ES6新增了Symbol)作为属性名(key);
- 某些情况下我们可能希望通过其他类型作为key,比如对象,这个时候会自动将对象转成字符串来作为key;
- 那么我们就可以使用Map:
代码使用
// 1.JavaScript中对象中是不能使用对象来作为key的 const obj1 = { name: "why" } const obj2 = { name: "kobe" } const info = { [obj1]: "aaa", [obj2]: "bbb" } console.log(info) // 2.Map就是允许我们对象类型来作为key的 // 构造方法的使用 const map = new Map() map.set(obj1, "aaa") map.set(obj2, "bbb") map.set(1, "ccc") console.log(map) const map2 = new Map([[obj1, "aaa"], [obj2, "bbb"], [2, "ddd"]]) console.log(map2) // Map(3) {{…} => 'aaa', {…} => 'bbb', 2 => 'ddd'}
Map的常见方法
- Map常见的属性:
- size:返回Map中元素的个数;
- Map常见的方法:
- set(key, value):在Map中添加key、value,并且返回整个Map对象;
- get(key):根据key获取Map中的value;
- has(key):判断是否包括某一个key,返回Boolean类型;
- delete(key):根据key删除一个键值对,返回Boolean类型;
- clear():清空所有的元素;
- forEach(callback, [, thisArg]):通过forEach遍历Map;
- Map也可以通过for of进行遍历。
代码演示
// 常见的属性和方法 console.log(map2.size) // set map2.set("why", "eee") console.log(map2) // get(key) console.log(map2.get("why")) // has(key) console.log(map2.has("why")) // delete(key) map2.delete("why") console.log(map2) // clear // map2.clear() // console.log(map2) // 遍历map map2.forEach((item, key) => { console.log(item, key) }) for (const item of map2) { console.log(item[0], item[1]) } for (const [key, value] of map2) { console.log(key, value) }
WeakMap
WeakMap的使用
- 和Map类型的另外一个数据结构称之为WeakMap,也是以键值对的形式存在的。
- 那么和Map有什么区别呢?
- 区别一:WeakMap的key只能使用对象,不接受其他的类型作为key;
- 区别二:WeakMap的key对对象想的引用是弱引用,如果没有其他引用引用这个对象,那么GC可以回收该对象;
- WeakMap常见的方法有四个:
- set(key, value):在Map中添加key、value,并且返回整个Map对象;
- get(key):根据key获取Map中的value;
- has(key):判断是否包括某一个key,返回Boolean类型;
- delete(key):根据key删除一个键值对,返回Boolean类型;
代码演示
const obj = {name: "obj1"} // 1.WeakMap和Map的区别二: const map = new Map() map.set(obj, "aaa") const weakMap = new WeakMap() weakMap.set(obj, "aaa") // 2.区别一: 不能使用基本数据类型 // weakMap.set(1, "ccc") // 3.常见方法 // get方法 console.log(weakMap.get(obj)) // has方法 console.log(weakMap.has(obj)) // delete方法 console.log(weakMap.delete(obj)) // WeakMap { <items unknown> } console.log(weakMap)
WeakMap的应用
- 注意:WeakMap也是不能遍历的
- 因为没有forEach方法,也不支持通过for of的方式进行遍历;
- 那么我们的WeakMap有什么作用呢?
代码演示
可以做对象的依赖收集
先把对象的属性和属性对应的依赖,存储为Map结构,一个key 对应一组收集的函数依赖
然后把对象 和Map 结构存储为weakMap
当代理的对象有变化时,我们去weakMap 取key,再执行依赖函数集
// 应用场景(vue3响应式原理) const obj1 = { name: "why", age: 18 } function obj1NameFn1() { console.log("obj1NameFn1被执行") } function obj1NameFn2() { console.log("obj1NameFn2被执行") } function obj1AgeFn1() { console.log("obj1AgeFn1") } function obj1AgeFn2() { console.log("obj1AgeFn2") } // 1.创建WeakMap const weakMap = new WeakMap() // 2.收集依赖结构 // 2.1.对obj1收集的数据结构 const obj1Map = new Map() obj1Map.set("name", [obj1NameFn1, obj1NameFn2]) obj1Map.set("age", [obj1AgeFn1, obj1AgeFn2]) weakMap.set(obj1, obj1Map) // 2.2如果obj1.name发生了改变 // Proxy/Object.defineProperty obj1.name = "james" const targetMap = weakMap.get(obj1) const fns = targetMap.get("name") fns.forEach(item => item())