前言
大家心里是否产生过这样的疑问:JS 中既然已经有对象这种数据结构,我们为什么还要再单独去使用 Set 或者 Map 呢?
今天我们来梳理一下其中的缘由,从出现的原因开始学习 Set 和 Map。
使用对象模拟集合的弊端
在 Set 和 Map 的标准出现,大家都会使用对象来模拟集合,比如:
var testObj = Object.create(null);
testObj.foo = 'bar';
if(testObj.foo) {
// 执行的一些代码
}
但这样的模拟方式,在某些时刻会产生一些如下的不可避免的问题。
数值类型的 key 会被自动转换为字符串
var testObj = Object.create(null);
testObj[1]= 'foo';
console.log(testObj['1']); // foo
无法用对象作为属性名
var obj1 = {};
var obj2 = {};
var testObj = Object.create(null);
testObj[obj1] = 'foo';
console.log(testObj[obj2]); // foo
原本我们期望使用不同的对象,作为不同的属性名,但是对象在这种情况下会被自动转换为字符串"[object Object]",所以两个原本不同的对象,指向了一个相同的属性。
属性值为布尔值时会自动转换为 true 或 false
本身这种逻辑是没有问题的,但若我们只是想要判断属性的有无,就会对我们的判断结果造成影响。
var testObj = Object.create(null);
testObj.count = 0;
if(testObj.count) {
console.log('hello')
}
// 什么都不会打印
综上,我们有了新的 Set 和 Map 标准,来更好应对不同的集合情况。
Set
Set 类型是一种有序的列表,包含相互独立的非重复值。
let set = new Set();
set.add(5);
set.add('5');
使用 add() 方法向集合中添加元素。字符串 5 不会被自动转换为数字类型。
- 如果相同的 key 添加第二次,那么后来的添加会被忽略。
- 用数组来初始化 Set 的时候,Set 的构造函数会过滤掉重复的值,保证集合中各元素的唯一。
set.delete(5); // 移除集合中的某一元素
set.clear(); // 移除集合中的所有元素
set.forEach(); // 三个参数:值、键名、集合本身
set.has(6); // 判断集合中是否有某个参数,返回 true 或 false
let set = new Set([4, 6, 8, 9, 2]); // 将数组转换为集合,自动排序去重
let arr = [...set]; // 将集合转换为数组
Weak Set
将对象存储在 Set 的实例与存储在 变量中一样,只要 Set 中的引用存在,垃圾回收机制就不能释放对应的内存。
而 Weak Set 只存储对象的弱引用,且不能存储原始值。当 Weak set 是对象的唯一引用时,就会被垃圾回收机制释放掉内存。
- Weak Set 不可迭代。
- Weak Set 不支持 forEach() 方法。
- Weak Set 不支持 size 属性。
Map
Map 类型是一种存储许多键值对的有序列表。键名和值支持所有数据类型。
let map = new Map();
map.set("hello", "world");
map.get("hello"); // 获取值
map.has(1); // 判断是否存在某个属性
map.delete("hello"); // 删除某个属性
map.clear(); // 移除所有键值对
Map 的 forEach() 与 Set 相似。
WeakMap 也可以直接参考 WeakSet。
本文探讨了在 JavaScript 中为何需要引入 ES6 的 Set 和 Map 数据结构,而非仅依赖对象。文章指出使用对象模拟集合存在的问题,如数值 key 转字符串、无法使用对象作为属性名及布尔值属性自动转换。接着,详细介绍了 Set 的特点,如存储非重复值,以及 WeakSet 的概念,其允许弱引用并节省内存。最后,概述了 Map 的功能,强调它支持任意类型的键值对,并对比了 WeakMap 的特性。
927

被折叠的 条评论
为什么被折叠?



