Set类型
定义
本身是一个构造函数,用来生成Set数据结构,可以接受一个数组(具有iterable接口的其他数据结构)作为参数,用来初始化。允许存储任何类型的值,无论是原始值或者是对象引用
🧐判断恒等特殊值
- +0 与 -0 在存储判断唯一性的时候是恒等的,所以不重复
undefined
与undefined
是恒等的,所以不重复
NaN
与NaN
是不恒等的,但是在Set
中认为NaN
与NaN
相等,所有只能存在一个,不重复
属性及方法
size
:返回集合所包含元素的数量
add(value)
:添加某个值,返回Set
结构本身(可以链式调用)
delete(value)
:删除某个值,删除成功返回true
,否则返false
has(value)
:返回一个布尔值,表示该值是否为Set
的成
clear()
:清除所有成员,没有返回值。
keys()
:返回键名的遍历器。
values()
:返回键值的遍历器。
entries()
:返回键值对的遍历器。每个键和值相等
forEach()
:使用回调函数遍历每个成员。
优势(相对数组)
set中的每一项都必须是唯一的
- 查看元素
- 删除元素:可以直接使用每项的value来删除该项
- 保存
NaN
:不能使用indexOf()
或者includes()
来查找NaN
,而Set
可以保存该值
- 删除重复项
- Set的时间复杂度为O(1),而数组为O(n)
代码例子
let arr = [], set = new Set(), n = 1000000; for (let i = 0; i < n; i++) { arr.push(i); set.add(i); }
查找元素
let result; console.time('Array'); result = arr.indexOf(123123) !== -1; console.timeEnd('Array'); console.time('Set'); result = set.has(123123); console.timeEnd('Set');
添加元素
console.time('Array'); arr.push(n); console.timeEnd('Array'); console.time('Set'); set.add(n); console.timeEnd('Set');
删除元素
const deleteFromArr = (arr, item) => { let index = arr.indexOf(item); return index !== -1 && arr.splice(index, 1); }; console.time('Array'); deleteFromArr(arr, n); console.timeEnd('Array'); console.time('Set'); set.delete(n); console.timeEnd('Set');
应用
Array.from
方法可以将Set
结构转为数组
- 数组去重
- 数组的
map
和filter
方法可以间接用于Set
let set = new Set([1, 2, 3]) set = new Set([...set].map((x) => x * 2)) // 返回Set结构:{2, 4, 6} let set = new Set([1, 2, 3, 4, 5]) set = new Set([...set].filter((x) => x % 2 == 0)) // 返回Set结构:{2, 4}
- 实现并集
(Union)
、交集(Intersect)
和差集
let a = new Set([1, 2, 3]) let b = new Set([4, 3, 2]) // 并集 let union = new Set([...a, ...b]) // Set {1, 2, 3, 4} // 交集 let intersect = new Set([...a].filter((x) => b.has(x))) // set {2, 3} // 差集 let difference = new Set([...a].filter((x) => !b.has(x))) // Set {1}
WeakSet类型
定义
结构与Set类似,不重复的值的集合
WeakSet 的成员只能是对象,而不能是其他类型的值
🧐特点:WeakSet 中的对象都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用
- 如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于 WeakSet 之中
- WeakSet 适合临时存放一组对象,以及存放跟对象绑定的信息。只要这些对象在外部消失,它在 WeakSet 里面的引用就会自动消失。
- 不可遍历⚠️
- 用来存这个对象相关的数据,与数据共存亡
方法
add(value)
:添加某个值
delete(value)
:删除某个值,删除成功返回true
,否则返false
has(value)
:返回一个布尔值
Map类型
应用
1. 储存 DOM 节点,而不用担心这些节点从文档移除时,会引发内存泄漏
<div id="root"> <button id="btn">11111</button> </div> <script> let wrap = document.getElementById("root"); let btn = document.getElementById("btn"); let element = new WeakSet(); console.log("btn", btn, wrap); element.add(btn); btn.addEventListener("click", () => { wrap.removeChild(btn); console.log("element", element); }); </script>
🔧tip:这里当 button 被移除,disabledElements 中的内容会因为是弱引用而直接变成空,也就是disabledElements被垃圾回收掉了其中的内存,避免了一个小小的内存泄漏的产生
2. 一个用户对象作为键,其访问次数为值。当一个用户离开时(该用户对象将被垃圾回收机制回收),这时我们就不再需要他的访问次数了
let visitsCountMap = new WeakMap() // 递归用户来访次数 function countUser(user){ let count = visitsCountMap.get(user) || 0 visitsCountMap.set(user, count + 1) } // 📁 main.js let john = { name: "John" }; countUser(john); // count his visits // 不久之后,john 离开了 john = null;
3. 缓存计算的结果
let cache = new WeakMap() // 与obj 嘻嘻相关的结果 function process(obj){ if(!cache.has(obj)) { let result = `与obj有关的计算` cache.set(obj, result) } return cache.get(obj) } // other.js let obj = {} let result1 = process(obj) let result2 = process(obj) obj = null // 如果是Map 就cache 里不可被回收
定义
类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,
const map = new Map([ ['name', '张三'], ['title', 'Author'] ]); map.size // 2 map.has('name') // true map.get('name') // "张三" map.has('title') // true map.get('title') // "Author"
Map和Object的区别
-
Object
对象有原型, 也就是说他有默认的key
值在对象上面, 除非我们使用Object.create(null)
创建一个没有原型的对象
- 在
Object
对象中, 只能把String
和Symbol
作为key
值, 但是在Map
中,key
值可以是任何基本类型(String
,Number
,Boolean
,undefined
,NaN
….),或者对象(Map
,Set
,Object
,Function
,Symbol
,null
….)
- 通过
Map
中的size
属性, 可以很方便地获取到Map
长度, 要获取Object
的长度, 你只能手动计算
属性及方法
size
:返回所包含元素的数量
set(key, val)
: 向Map
中添加新元素
get(key)
: 通过键值查找特定的数值并返回
has(key)
: 判断Map
对象中是否有Key
所对应的值,有返回true
,否则返回false
delete(key)
: 通过键值从Map
中移除对应的数据
clear()
: 将这个Map
中的所有元素删除
keys()
:返回键名的遍历器
values()
:返回键值的遍历器
entries()
:返回键值对的遍历器
forEach()
:使用回调函数遍历每个成员
转化
// Map 转为数组 let map = new Map() let arr = [...map] // 数组转为 Map Map: map = new Map(arr) // Map 转为对象 let obj = {} for (let [k, v] of map) { obj[k] = v } // 对象转为 Map for( let k of Object.keys(obj)){ map.set(k,obj[k]) }
应用
例如个人信息的展示,通过Map 来改造,将我们需要显示的 label 和 value 存到我们的 Map 后渲染到页面,这样减少了大量的html代码
<div class="info-item"> <span>姓名</span> <span>{{info.name}}</span> </div> <div class="info-item"> <span>年龄</span> <span>{{info.age}}</span> </div> <div class="info-item"> <span>性别</span> <span>{{info.sex}}</span> </div> <div class="info-item"> <span>手机号</span> <span>{{info.phone}}</span> </div> <div class="info-item"> <span>家庭住址</span> <span>{{info.address}}</span> </div> <div class="info-item"> <span>家庭住址</span> <span>{{info.duty}}</span> </div>
import "./styles.css"; export default function App() { let infoMap = {}; const info = { name: "jack", sex: "男", age: "28", phone: "13888888888", address: "广东省广州市", duty: "总经理" }; const mapKeys = ["姓名", "性别", "年龄", "电话", "家庭地址", "身份"]; let result = new Map(); let i = 0; for (const key in info) { result.set(mapKeys[i], info[key]); i++; } infoMap = result; console.log([...infoMap]); return ( <div className="App"> {[...infoMap].map((item) => ( <> <span>{item[0]}</span> <span>{item[1]}</span> </> ))} </div> ); }
WeakMap
WeakMap
结构与 Map
结构类似,也是用于生成键值对的集合。- 只接受对象作为键名(
null
除外),不接受其他类型的值作为键名
- 键名是弱引用,键值可以是任意的,键名所指向的对象可以被垃圾回收,此时键名是无效的
- 不能遍历,方法有
get
、set
、has
、delete