文章目录
- Map
- 特点
- 用法
- 举例
- 注意
- Map与Object
- Set
- 特点
- 用法
- 举例
- 注意
- Set与Array
- WeakMap
- 特点
- 用法
- 注意
- WeakSet
- 特点
- 用法
- 注意
- 总结
Map 和 Set 是 ES6 标准新增的数据结构,它们分别提供了键值对和唯一值的集合。
Map
Map是一组键值对的结构,具有极快的查找速度。
Map 对象保存键值对,并且能够记住键的原始插入顺序。任何值(对象或者原始值)都可以作为一个键或一个值。
特点
- 键值对集合
- 键的顺序与插入顺序相同
- 键可以是任何类型(对象、基本类型)
- 键是唯一的
如果要实现一个“名字”-“年龄”
的对照表,直接根据名字查找年龄,无论这个表有多大,查找速度都不会变慢。
用JavaScript写一个Map如下:
let person = new Map([['张三', 20], ['李四', 22], ['王五', 24]]);
person.get('张三'); // 20
用法
let m = new Map(); // 空Map
// 设置键值对
m.set('aaa', 111);
m.set('bbb', 222);
// 检查键是否存在
m.has('aaa'); // true
// 获取值
m.get('aaa'); // 111
// 删除键值对
m.delete('aaa'); // 'aaa'
m.get('aaa'); // undefined
// 由于一个key只能对应一个value,所以,多次对一个key放入value,后面的值会把前面的值覆盖掉:
m.set('bbb', 333);
m.get('bbb'); // 333
// 获取大小
console.log(m.size); // 1
// 遍历Map
for (let [key, value] of m) {
console.log(key, value); // bbb, 333
}
// 清除所有键值对
m.clear();
console.log(m.size); // 0
m.has('bbb'); // false
举例
const userMap = new Map();
userMap.set('Alice', { age: 25, country: 'USA' });
userMap.set('Bob', { age: 30, country: 'Canada' });
for (let [name, info] of userMap) {
console.log(`${name} is ${info.age} years old and from ${info.country}.`);
}
注意
- 当使用对象作为键时,JavaScript 会在内部使用对象的引用而不是对象的值。
因此,即使两个对象的内容相同,它们也会被视为不同的键。 - Map 的迭代顺序是插入顺序。
Map与Object
- 存储方式:
Map使用键值对的方式存储数据,其中键和值可以是任何类型,且键必须是唯一的。
Object也使用键值对的方式存储数据,但其键通常是字符串或Symbol类型。 - 排序:
Map保持插入顺序,因此迭代时元素会按照插入的顺序出现。
Object不保证属性的顺序。 - 性能:
当键是简单类型时,Object的键查找速度可能更快。
但对于复杂类型或需要排序的场景,Map可能更有优势。 - 使用场景:
Map适用于需要存储键值对,且键类型多样或需要保持插入顺序的场景。
Object更常用于表示具有属性的对象或数据结构。
Set
Set 和 Map 类似,也是一组 key 的集合,但不存储 value 。由于 key 不能重复,所以,在 Set 中,没有重复的 key。
Set 对象是一种值的集合,它的值都是唯一的,没有重复的值。
特点
- 值的集合
- 值是唯一的,没有重复
- 插入顺序是保持的(在某些实现中)
用法
要创建一个Set,需要提供一个Array作为输入,或者直接创建一个空Set:
var s1 = new Set(); // 空Set
var s2 = new Set([1, 2, 3]); // 含1, 2, 3
// 重复元素在Set中自动被过滤
var s = new Set([1, 2, 3, 3, '3']);
console.log(s); // Set {1, 2, 3, "3"} 注意数字`3`和字符串`'3'`是不同的元素
// 添加元素到 Set 中,可以重复添加,但不会有效果
s.add(4);
console.log(s); // Set {1, 2, 3, 4}
s.add(4);
console.log(s); // 仍然是 Set {1, 2, 3, 4}
// 删除元素
var s = new Set([1, 2, 3]);
console.log(s); // Set {1, 2, 3}
s.delete(3);
console.log(s); // Set {1, 2}
// 检查值是否存在
console.log(s.has(2)); // true
// 获取大小
console.log(s.size); // 2
// 遍历Set
for (let value of s) {
console.log(value);
}
// 清除所有元素
s.clear();
console.log(s.size); // 0
s.has(1); // false
举例
由于key不能重复,所以常用作数组去重
var s = new Set([1, 2, 2, 3, 'x', 4, 3, 2, 1, 'x']);
// ES6拓展运算符
console.log([...s]); // [1, 2, 3, "x", 4]
// or
console.log(Array.from(s)); // [1, 2, 3, "x", 4]
注意
- Set 中的值必须是唯一的。尝试添加重复的值不会有任何效果。
- 在某些JavaScript引擎中(如V8),Set 的迭代顺序是插入顺序,但这并不是ECMAScript规范的要求。如果需要保证顺序,应使用其他方法,如数组。
- 与 Map 一样,当使用对象作为 Set 的值时,对象的引用被用作唯一性的判断标准,而不是对象的内容。
Set与Array
- 存储方式:
Set是一个值的集合,它的成员值都是唯一的,没有重复的值。
Array则是一个有序的元素序列,可以存储重复的值。 - 排序:
Set不保证元素的顺序。
Array中的元素是有序的。 - 操作方法:
Set提供了一些特有的方法,如add()添加元素、delete()删除元素、has()检查元素是否存在等。
Array则具有更多的操作方法,如push()、pop()、shift()、unshift()等用于添加和删除元素,以及sort()、reverse()等用于修改元素顺序。 - 使用场景:
Set适用于需要存储一组唯一值,且不关心顺序的场景。
Array则更常用于存储和处理有序的数据集合。
总之,Map 和 Set 提供了对键值对和唯一值集合的有效管理,是JavaScript中非常有用的数据结构。
WeakMap
WeakMap是一个键值对的集合,其中的键是弱引用的。
这意味着,当键所指向的对象没有其他引用时,它会被垃圾回收器自动回收,同时WeakMap中对应的键值对也会被移除。
特点
- 键只能是对象,不能是原始值(如字符串、数字等)。
- 键是弱引用,会被垃圾回收。
- 键值对是不可枚举的,不能使用常规的遍历方法。
- 可以有效防止内存泄漏。
用法
const wm = new WeakMap();
let obj1 = { name: 'John' };
let obj2 = { name: 'Jane' };
// 设置键值对 key必须是对象,value可以是任意类型。如果key已经存在,则更新其对应的value。
wm.set(obj1, 10);
wm.set(obj2, 20);
// 获取值
wm.get(obj1); // 10
wm.get(obj2); // 20
wm.get({ name: 'Json' }) // undefined 如果key不存在,则返回undefined
// 检查键是否存在
wm.has(obj1); // true
wm.has({ name: 'John' }) // false
// 删除键值对 如果删除成功,返回true;如果key不存在,返回false。
wm.delete(obj2); // 返回 true
wm.get(obj2); // undefined
obj1 = null; // 释放对obj1的引用 obj1将被垃圾回收,wm中对应的键值对也会被移除
wm.get(obj1); // undefined
注意
- 由于WeakMap的键是弱引用,因此不应该依赖于键的持久存在。
- WeakMap不支持遍历,如果需要遍历,请考虑使用常规的Map。
WeakSet
WeakSet是一个值的集合,其中的值是弱引用的。
这意味着,当集合中的某个值没有其他引用时,它会被垃圾回收器自动回收。
特点
- 成员只能是对象,不能是原始值(如字符串、数字等)。
- 成员是弱引用,会被垃圾回收。
- 不支持遍历,也没有size属性。
用法
const ws = new WeakSet();
let obj1 = { name: 'Alice' };
let obj2 = { name: 'Bob' };
// 设置一个新的对象 如果添加成功,返回WeakSet本身;如果value已经存在,则不会重复添加。
ws.add(obj1);
ws.add(obj2);
// 检查键是否存在
ws.has(obj1); // true
ws.has({ name: 'Alice' }); // false
// 删除 如果删除成功,返回true;如果value不存在,返回false。
ws.delete(obj2); // true
ws.has(obj2); // false
obj1 = null; // 释放对obj1的引用
// obj1将被垃圾回收,ws中对应的值也会被移除
ws.has(obj1); // false
注意
- WeakSet主要用于存储对象,并且这些对象在其他地方没有强引用。
- 由于WeakSet不支持遍历,因此不应用于需要频繁访问或修改集合成员的场景。
总的来说,WeakMap和WeakSet在处理垃圾回收和内存管理方面提供了更灵活的方式,特别适用于需要存储临时数据或避免内存泄漏的场景。但是,由于它们的特殊性质(如不支持遍历),使用时需要特别注意。
总结
Map、WeakMap、Set和WeakSet都是JavaScript中用于存储数据的结构,但它们之间存在一些关键的区别。
- Map和Set都是普通的键值对或值的集合。
Map是一个键值对的集合,其中的键和值可以是任何类型的对象,且键是唯一的。Map允许你通过键来存储和检索值,因此可以通过get方法获取值。同时,Map的键值对可以通过迭代器进行遍历。
Set则是一个值的集合,它的值也是唯一的,因此可以用于数组去重。Set中的元素不可重复且会自动排序。 - WeakMap和WeakSet是Map和Set的“弱”版本。它们的主要区别在于它们对对象的引用方式。
在WeakMap中,键对对象的引用是弱引用,这意味着如果一个对象只被WeakMap引用,那么这个对象可以被垃圾回收。这有助于防止因长时间持有对象引用而导致的内存泄漏。另外,WeakMap的键是不可遍历的,因此它常被用来保存对象的私有数据。
WeakSet只能存储对象,且这些对象也是弱引用关联的,即如果没有其他引用指向该对象,则该对象会被垃圾回收。但WeakSet的一个主要限制是它没有迭代器,因此不能遍历其中的元素。
总的来说,Map和Set提供了强大的键值对或值集合的存储和检索能力,而WeakMap和WeakSet则提供了对对象进行弱引用的机制,有助于更有效地管理内存和数据的私有性。选择使用哪种结构取决于你的具体需求,比如是否需要存储键值对、是否需要弱引用、是否需要遍历等。