Skip to content

ES6 - 集合

ES6+ - 现代集合:Map, Set, WeakMap, WeakSet

Section titled “ES6+ - 现代集合:Map, Set, WeakMap, WeakSet”

ES6 引入了强大的新型内置数据结构:Map、Set、WeakMap 和 WeakSet。与传统的对象 (object) 和数组 (array) 相比,它们提供了更灵活的方式来组织和管理数据集合。

Map 对象保存键/值对 (key-value pairs),并记住键的原始插入顺序。任何值(包括对象和原始值 primitive value)都可以用作键或值。这相较于普通对象是一个显著优势,因为普通对象的键会被隐式转换为字符串。

语法:`new Map([iterable])`
其中 `iterable` 是一个数组 (`Array`) 或其他可迭代对象 (`iterable object`),其元素是键值对(包含两个元素的数组,如 `[key, value]`)。
  • map.size:返回 Map 中的键/值对数量。
  • map.set(key, value):添加或更新指定键和值的元素。返回 Map 对象,允许链式调用 (chaining)。
  • map.get(key):返回与键关联的值,如果找不到键则返回 undefined。
  • map.has(key):返回一个布尔值,指示是否存在指定键的元素。
  • map.delete(key):移除具有指定键的元素。如果元素存在并被移除,则返回 true,否则返回 false。
  • map.clear():移除 Map 对象中的所有键/值对。

迭代方法(都返回迭代器 Iterator):

Section titled “迭代方法(都返回迭代器 Iterator):”
  • map.keys():返回一个新的迭代器对象,该对象包含 Map 中按插入顺序排列的所有键。
  • map.values():返回一个新的迭代器对象,该对象包含 Map 中按插入顺序排列的所有值。
  • map.entries():返回一个新的迭代器对象,该对象包含 Map 中按插入顺序排列的每个元素的 [key, value] 数组。这是 Map 的默认迭代器 (default iterator)。

map.forEach(callbackFn[, thisArg]):按插入顺序对 Map 中的每个键值对执行一次 callbackFn(回调函数)。

const userRoles = new Map();
// Set key-value pairs
userRoles.set('admin', 'Administrator Access');
userRoles.set('editor', 'Content Editor Access');
userRoles.set(123, 'Numeric Key Example'); // Keys can be non-strings
const user = { id: 1 };
userRoles.set(user, 'Object Key Example'); // Keys can be objects
console.log(userRoles.get('admin')); // Output: Administrator Access
console.log(userRoles.get(user)); // Output: Object Key Example
console.log(userRoles.has('guest')); // Output: false
console.log(userRoles.size); // Output: 4
// Initialize with an array of key-value pairs
const settings = new Map([
['theme', 'dark'],
['fontSize', 16]
]);
console.log(settings.get('theme')); // Output: dark
// Iterating over a Map using for...of (default is entries)
for (const [key, value] of userRoles) {
console.log(`${typeof key} ${key}: ${value}`);
}
// Example output for one entry: string admin: Administrator Access
// Iterating using forEach
settings.forEach((value, key) => {
console.log(`Setting - ${key}: ${value}`);
});
// Output:
// Setting - theme: dark
// Setting - fontSize: 16

WeakMap 是一个键/值对的集合,其中的键必须是对象,值可以是任意类型。对键的引用是“弱引用” (weakly) 的,这意味着如果没有其他引用指向某个键对象,该对象就可以被垃圾回收 (garbage collected),并且它在 WeakMap 中的对应条目也会被自动移除。这使得 WeakMap 非常适合将数据与对象关联起来,而不会阻止这些对象被垃圾回收。

WeakMap 的主要特点:

  • 键必须是对象。不允许使用原始值作为键。
  • 条目不可枚举 (enumerable)。你不能迭代遍历 WeakMap(没有 keys()、values()、entries()、forEach() 或 size 属性)。
  • 只有 get(key)、set(key, value)、has(key) 和 delete(key) 方法可用。
let activeUsers = new WeakMap();
let user1 = { name: 'Alice' };
let user2 = { name: 'Bob' };
activeUsers.set(user1, { lastLogin: Date.now() });
activeUsers.set(user2, { status: 'online' });
console.log(activeUsers.has(user1)); // Output: true
console.log(activeUsers.get(user1)); // Output: { lastLogin: ... }
// If user1 becomes eligible for garbage collection (e.g., user1 = null and no other references exist),
// its entry in activeUsers will eventually be removed by the garbage collector.
user1 = null;
// At some point, activeUsers.has(the_original_user1_object) would be false.

Set 对象允许你存储任何类型的唯一值,无论是原始值还是对象引用 (object references)。Set 中的值按照插入顺序排列。

语法:`new Set([iterable])`
其中 `iterable` 是一个数组 (`Array`) 或其他可迭代对象,其元素用于初始化集合。
  • set.size:返回 Set 中唯一值的数量。
  • set.add(value):将 value 添加到 Set。返回 Set 对象(可链式调用)。如果 value 已存在,则不做任何更改。
  • set.has(value):返回一个布尔值,指示是否存在具有指定值的元素。
  • set.delete(value):从 Set 中移除指定的值。如果值找到并被移除,则返回 true,否则返回 false。
  • set.clear():移除 Set 对象中的所有元素。
  • set.values():返回一个新的迭代器对象,该对象包含 Set 中按插入顺序排列的所有值。这是 Set 的默认迭代器。
  • set.keys():是 values() 的别名,行为完全相同。
  • set.entries():返回一个新的迭代器对象,该对象包含 Set 中按插入顺序排列的每个元素的 [value, value] 数组(为了与 Map 保持一致)。

set.forEach(callbackFn[, thisArg]):按插入顺序对 Set 中的每个值执行一次 callbackFn。回调函数接收 (value, key, set),其中 key 与 value 相同。

const uniqueNumbers = new Set([1, 2, 2, 3, 4, 4, 4]);
console.log(uniqueNumbers.size); // Output: 4 (duplicates are ignored)
uniqueNumbers.add(5);
uniqueNumbers.add(1); // Already exists, no change
console.log(uniqueNumbers.has(3)); // Output: true
uniqueNumbers.delete(2);
console.log(uniqueNumbers.has(2)); // Output: false
// Iterating over a Set using for...of (default is values)
for (const number of uniqueNumbers) {
console.log(number);
}
// Output (in insertion order, e.g.): 1, 3, 4, 5
// Iterating using forEach
uniqueNumbers.forEach(value => {
console.log(`Set value: ${value}`);
});

WeakSet 仅存储对象。类似于 WeakMap,对 WeakSet 中对象的引用是“弱引用”的。如果 WeakSet 中的对象没有其他引用指向它,它就可以被垃圾回收。

WeakSet 的主要特点:

  • 只能存储对象。不允许使用原始值。
  • 条目不可枚举。你不能迭代遍历 WeakSet(没有迭代器、forEach 或 size)。
  • 只有 add(value)、has(value) 和 delete(value) 方法可用。
let processedObjects = new WeakSet();
let obj1 = { id: 'A' };
let obj2 = { id: 'B' };
function processObject(obj) {
if (!processedObjects.has(obj)) {
console.log(`Processing object: ${obj.id}`);
// ... perform processing ...
processedObjects.add(obj);
} else {
console.log(`Object ${obj.id} already processed.`);
}
}
processObject(obj1);
processObject(obj2);
processObject(obj1); // Will indicate it's already processed
// If obj1 becomes eligible for GC, its entry in processedObjects is removed.
obj1 = null;

ES6+ 中的所有标准集合(Array、Map、Set、String)都是可迭代的 (iterable)。这意味着它们实现了迭代器协议 (iterator protocol)。迭代器 (iterator) 是一个知道如何一次访问集合中一个项目,并跟踪其在序列中当前位置的对象。迭代器有一个 next() 方法,该方法返回一个具有两个属性的对象:value(序列中的下一个项目)和 done(一个布尔值,如果迭代器已到达序列末尾则为 true)。

const myMap = new Map([['a', 1], ['b', 2]]);
const mapIterator = myMap.entries(); // Get an iterator
console.log(mapIterator.next()); // { value: ['a', 1], done: false }
console.log(mapIterator.next()); // { value: ['b', 2], done: false }
console.log(mapIterator.next()); // { value: undefined, done: true }
const mySet = new Set(['apple', 'banana']);
const setIterator = mySet.values();
console.log(setIterator.next()); // { value: 'apple', done: false }
console.log(setIterator.next()); // { value: 'banana', done: false }
console.log(setIterator.next()); // { value: undefined, done: true }

虽然手动迭代是可能的,但 for...of 循环是处理可迭代对象最常见和最方便的方式。