avatar
ECMAScript
🌑

ECMAScript

keywords
JavaScript, Set, WeakSet, Map, WeakMap
published_date
最新编辑 2024年10月09日
Created time
2021年12月21日
slug
本文介绍了ECMAScript中的Set、WeakSet、Map和WeakMap类型,包括它们的定义、属性、方法及应用。Set用于存储唯一值,支持操作如添加、删除和查找,且时间复杂度为O(1)。WeakSet仅存储对象并具有弱引用特性。Map允许使用任意类型的值作为键,提供了丰富的方法来操作键值对。WeakMap与Map类似,但键名是弱引用,适合存储临时数据,避免内存泄漏。
tags
JavaScript

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');
notion image
添加元素
console.time('Array'); arr.push(n); console.timeEnd('Array'); console.time('Set'); set.add(n); console.timeEnd('Set');
notion image
删除元素
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');
notion image

应用

  1. Array.from 方法可以将 Set 结构转为数组
  1. 数组去重
  1. 数组的 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}
  1. 实现并集 (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>
notion image
notion image
🔧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的区别

  1. Object 对象有原型, 也就是说他有默认的 key 值在对象上面, 除非我们使用 Object.create(null)创建一个没有原型的对象
  1. Object 对象中, 只能把 StringSymbol 作为 key 值, 但是在 Map 中,key 值可以是任何基本类型(String, Number, Boolean, undefined, NaN….),或者对象(Map, Set, Object, Function , Symbol , null….)
  1. 通过 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 除外),不接受其他类型的值作为键名
  • 键名是弱引用,键值可以是任意的,键名所指向的对象可以被垃圾回收,此时键名是无效的
  • 不能遍历,方法有 getsethasdelete

关于我

My desire to practice my skills and share my acquired knowledge fuels my endeavors.

联系 : znjessie858@gmail.com

订阅

订阅我的文章,收到我的最新发布通知,可随时取消订阅!