JS中的WeakSet介绍与使用
一、基本介绍
ES6新增的“弱集合”是一种新的集合类型,为这门语言带来了集合数据结构。WeakSet是Set的“兄弟"类型,其API也是Set的子集。WeakSet中的”weak“(弱),描述的是JavaScript垃圾回收程序对待”弱集合”中值的方式。
二、基本API
可以使用new关键字实例化一个空的WeakSet;
const ws=new WeakSet();
弱集合中的值只能是Object或者继承自Object的类型,尝试使用非对象设置值会抛出TypeError。
如果想在初始化时填充弱集合,则构造函数可以接收一个可迭代对象,其中需要包含有效的值。可迭代对象中的每个值都会按照迭代顺序插入到新实例中;
const val1={id:1}, val2={id:2}, val3={id:3} //使用数组初始化弱集合 const ws1=new WeakSet([val1,val2,val3]); alert(ws1.has(val1)); //true alert(ws1.has(val2)); //true alert(ws1.has(val3)); //true //初始化是全有或全无的操作 //只要有一个值无效就会抛出错误,导致整个初始化失败 const ws2=new WeakSet([val1,"BADVAL",val3]); //TypeError: Invalid value used in WeakSet typeof ws2; // ReferenceError:ws2 is not defined //原始值可以先包装成对象再用作值 const stringVal=new String("val1"); const ws3=new WeakSet([stringVal]); alert(ws3.has(stringVal)); //true
初始化之后可以使用add()再添加新值,可以使用has()查询,还可以使用delete()删除:
const ws=new WeakSet(); const val1={id:1}, val2={id:2}; alert(ws.has(val1)); //false ws.add(val1).add(val2); alert(ws.has(val1)); //true alert(ws.has(val2)); //true ws.delete(val); //只删除这一个值 alert(ws.has(val1)); //false alert(ws.has(val2)); //true
add()方法返回弱集合实例,因此可以把多个操作连缀起来,包括初始化声明:
const val={id:1}, va2={id:2}, val3={id:3} const ws=new WeakSet().add(val1); ws.add(val2).add(val3); alert(ws.has(val1)); //true alert(ws.has(val2)); //true alert(ws.has(val3)); //true
三、弱值
WeakSet中"weak"表示弱集合的值是“弱弱地拿着”的。意思就是,这些值不属于正式的引用,不会阻止垃圾回收。
来看下面的例子:
const ws=new WeakSet();
ws.add({});
add()方法初始化了一个新对象,并将它用作一个值。因为没有指向这个对象的其他引用,所以当这行代码执行完成后,这个对象值就会被当作垃圾回收。然后,这个值就从弱集合中消失了,使其成为了一个空集合。
再看一个稍微不同的例子:
const ws=new WeakSet(); const container={val:{}}; ws.add(container.val); function removeReference(){ container.val=null; }
这一次,container对象维护着一个对弱集合值的引用,因此这个对象值不会成为垃圾回收的目标。不过,如果调用了removeReference(),就会摧毁值对象的最后一个引用,垃圾回收程序就可以把这个值清理掉。
四、不可迭代值
因为WeakSet中的值任何时候都可能被摧毁,所以没必要提供迭代其值的能力。当然,也用不着像clear()这样一次性销毁所有值的方法。WeakSet确实没有这个方法。因为不可能迭代,所以也不可能在不知道对象引用的情况下从弱集合中取得值。即便代码可以访问WeakSet实例,也没办法看到其中的内容。
WeakSet之所以限制只能用对象作为值,是为了保证只有通过值对象的引用才能取得值。如果允许原始值,那就没办法区分初始化时使用的字符串字面量和初始化之后使用的有一个相等的字符串了。
五、使用弱集合
相比于WeakMap实例,WeakSet实例的用处没有那么大。不过,弱集合在给对象打标签时还是有价值的。
来看下面的例子,这里使用了一个普通Set:
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数中被删除,垃圾回收程序就可以忽略其存在,而立即释放其内存。(假设其他地方没有引用这个对象)
五、迭代与扩展操作
ES6新增的迭代器和扩展操作符对集合引用类型特别有用。这些新特性让集合类型之间相互操作、复制和修改变得异常方便。
如本章前面所示,有4种原生集合类型定义了默认迭代器:
1)Array
2)所有定型数组
3)Map
4)Set
很简单,这意味着上述所有类型都支持顺序迭代,都可以传入for-of循环
let iterableThings=[ Array.of(1,2), typeArr=Int16Array.of(3,4), new Map([[5,6],[7,8]]) ]; for (const iterableThing of iterableThings){ for(const x of iterableThing){ console.log(x); } } //1 //2 //3 //4 //[5,6] //[7,8] //9 //10
这也意味着所有这些类型都兼容扩展操作符。扩展操作符在对可迭代对象执行浅复制时特别有用,只需简单的语法就可以复制整个对象:
let arr1=[1,2,3] let arr2=[...arr1]; console.log(arr1); //[1,2,3] console.log(arr2); //[1,2,3] console.log(arr1===arr2) //false
对于期待可迭代对象的构造函数,只要传入一个可迭代对象就可以实现复制:
let map1=new Map([[1,2],[3,4]]); let map2=new Map(map1); console.log(map1); //Map(1=>2,3=>4) console.log(map2); //Map(1=>2,3=>4)
当然,也可以构建数组的部分元素:
let arr1=[1,2,3] let arr2=[0,...arr1,4,5] console.log(arr2); //[0,1,2,3,4,5]
浅复制意味着只会复制对象引用:
let arr1=[{}]; let arr2=[...arr1]; arr1[0].foo='bar'; console.log(arr2[0]); //{foo:'bar'}
上面的这些类型都支持多种构建方法,比如Array.of()和Array.from()静态方法。在与扩展操作符一起使用时,可以非常方便地实现互操作。
let arr1=[1,2,3]; //把数组复制到定型数组 let typedArr1=Int16Array.of(...arr1); let typedArr2=Int16Array.from(arr1); console.log(typedArr1); //Int16Array[1,2,3] console.log(typedArr2); //Int16Array[1,2,3] //把数组复制到映射 let map=new Map(arr1.map((x)=>[x,"val"+x])); console.log(map); map{1=>"val 1",2=>"val 2",3=>"val 3"} //把数组复制到集合 let set=new Set(typedArr2); console.log(set); //Set (1,2,3) //把集合复制回数组 let arr2=[...set] console.log(arr2); //[1,2,3]
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?