Set 和 Map 是 ES6 引入的新的数据结构,用于存储唯一值和键值对。
Set 是值的集合,每个值只能出现一次。
const set = new Set();
set.add(1);
set.add(2);
set.add(2); // 重复值,不会添加
console.log(set.size); // 2
// 使用数组初始化
const set2 = new Set([1, 2, 3, 3, 4]);
console.log([...set2]); // [1, 2, 3, 4]
const set = new Set([1, 2, 3]);
// 添加元素
set.add(4);
console.log(set.size); // 4
// 删除元素
set.delete(2);
console.log(set.has(2)); // false
// 检查是否存在
console.log(set.has(1)); // true
// 清空
set.clear();
console.log(set.size); // 0
const set = new Set([1, 2, 3]);
// for...of
for (const value of set) {
console.log(value);
}
// forEach
set.forEach(value => {
console.log(value);
});
// 转换为数组
const arr = [...set];
const arr2 = Array.from(set);
const arr = [1, 2, 2, 3, 3, 3, 4];
const unique = [...new Set(arr)];
console.log(unique); // [1, 2, 3, 4]
const str = 'hello';
const unique = [...new Set(str)].join('');
console.log(unique); // 'helo'
// 并集
function union(setA, setB) {
return new Set([...setA, ...setB]);
}
// 交集
function intersection(setA, setB) {
return new Set([...setA].filter(x => setB.has(x)));
}
// 差集
function difference(setA, setB) {
return new Set([...setA].filter(x => !setB.has(x)));
}
const set1 = new Set([1, 2, 3]);
const set2 = new Set([2, 3, 4]);
console.log([...union(set1, set2)]); // [1, 2, 3, 4]
console.log([...intersection(set1, set2)]); // [2, 3]
console.log([...difference(set1, set2)]); // [1]
function processItems(items) {
const processed = new Set();
for (const item of items) {
if (!processed.has(item)) {
process(item);
processed.add(item);
}
}
}
WeakSet 是 Set 的弱引用版本,只能存储对象。
const weakSet = new WeakSet();
const obj1 = {};
const obj2 = {};
weakSet.add(obj1);
weakSet.add(obj2);
console.log(weakSet.has(obj1)); // true
weakSet.delete(obj1);
console.log(weakSet.has(obj1)); // false
// 注意:WeakSet 不可迭代,没有 size 属性
特点:
Map 是键值对的集合,任何值都可以作为键。
const map = new Map();
map.set('name', 'Alice');
map.set('age', 25);
console.log(map.get('name')); // 'Alice'
console.log(map.size); // 2
// 使用数组初始化
const map2 = new Map([
['name', 'Alice'],
['age', 25]
]);
const map = new Map();
// 设置键值对
map.set('key1', 'value1');
map.set('key2', 'value2');
// 获取值
console.log(map.get('key1')); // 'value1'
// 检查键是否存在
console.log(map.has('key1')); // true
// 删除键值对
map.delete('key1');
// 清空
map.clear();
// 获取大小
console.log(map.size);
const map = new Map([
['name', 'Alice'],
['age', 25]
]);
// 遍历键值对
for (const [key, value] of map) {
console.log(key, value);
}
// forEach
map.forEach((value, key) => {
console.log(key, value);
});
// 获取所有键
for (const key of map.keys()) {
console.log(key);
}
// 获取所有值
for (const value of map.values()) {
console.log(value);
}
// 获取所有条目
for (const entry of map.entries()) {
console.log(entry);
}
| 特性 | Map | Object | |------|-----|--------| | 键类型 | 任何值 | 字符串或 Symbol | | 大小 | size 属性 | 手动计算 | | 迭代 | 可迭代 | 需要 Object.keys() | | 性能 | 频繁增删更好 | 频繁增删较差 | | 默认键 | 无 | 有原型链 |
// Map 可以使用任何类型作为键
const map = new Map();
map.set(1, 'number');
map.set(true, 'boolean');
map.set({}, 'object');
map.set(function() {}, 'function');
// Object 的键会被转换为字符串
const obj = {};
obj[1] = 'number';
obj[true] = 'boolean';
console.log(obj); // { '1': 'number', 'true': 'boolean' }
const cache = new Map();
function expensiveOperation(key) {
if (cache.has(key)) {
return cache.get(key);
}
const result = computeExpensiveValue(key);
cache.set(key, result);
return result;
}
function countOccurrences(arr) {
const counts = new Map();
for (const item of arr) {
counts.set(item, (counts.get(item) || 0) + 1);
}
return counts;
}
const arr = ['a', 'b', 'a', 'c', 'b', 'a'];
const counts = countOccurrences(arr);
console.log(counts.get('a')); // 3
const userMetadata = new Map();
const user1 = { id: 1, name: 'Alice' };
const user2 = { id: 2, name: 'Bob' };
userMetadata.set(user1, { lastLogin: Date.now(), role: 'admin' });
userMetadata.set(user2, { lastLogin: Date.now(), role: 'user' });
console.log(userMetadata.get(user1)); // { lastLogin: ..., role: 'admin' }
const elementData = new Map();
const button = document.querySelector('button');
elementData.set(button, { clicks: 0, lastClick: null });
button.addEventListener('click', () => {
const data = elementData.get(button);
data.clicks++;
data.lastClick = Date.now();
elementData.set(button, data);
});
class LRUCache {
constructor(capacity) {
this.capacity = capacity;
this.cache = new Map();
}
get(key) {
if (!this.cache.has(key)) {
return -1;
}
const value = this.cache.get(key);
// 移动到末尾(最近使用)
this.cache.delete(key);
this.cache.set(key, value);
return value;
}
put(key, value) {
if (this.cache.has(key)) {
this.cache.delete(key);
} else if (this.cache.size >= this.capacity) {
// 删除最久未使用的(第一个)
const firstKey = this.cache.keys().next().value;
this.cache.delete(firstKey);
}
this.cache.set(key, value);
}
}
WeakMap 是 Map 的弱引用版本,键必须是对象。
const weakMap = new WeakMap();
const obj1 = {};
const obj2 = {};
weakMap.set(obj1, 'value1');
weakMap.set(obj2, 'value2');
console.log(weakMap.get(obj1)); // 'value1'
weakMap.delete(obj1);
console.log(weakMap.has(obj1)); // false
// 注意:WeakMap 不可迭代,没有 size 属性
特点:
应用场景:
// 私有数据存储
const privateData = new WeakMap();
class Person {
constructor(name) {
privateData.set(this, { name });
}
getName() {
return privateData.get(this).name;
}
}
const person = new Person('Alice');
console.log(person.getName()); // 'Alice'
// person.name 不存在,实现了私有属性
const set = new Set();
set.add(NaN);
set.add(NaN);
console.log(set.size); // 1 (NaN === NaN 为 false,但 Set 认为相等)
set.add(0);
set.add(-0);
console.log(set.size); // 2 (0 === -0 为 true,但 Set 认为相等)
const map = new Map();
const obj1 = { id: 1 };
const obj2 = { id: 1 };
map.set(obj1, 'value1');
map.set(obj2, 'value2');
console.log(map.get(obj1)); // 'value1'
console.log(map.get(obj2)); // 'value2'
// 不同的对象引用,即使内容相同也是不同的键
// Map 在频繁增删时性能更好
const map = new Map();
for (let i = 0; i < 1000000; i++) {
map.set(i, i);
}
// Object 在频繁增删时性能较差
const obj = {};
for (let i = 0; i < 1000000; i++) {
obj[i] = i;
}
const map = new Map([['name', 'Alice']]);
console.log(JSON.stringify(map)); // '{}' (Map 不能直接序列化)
// 需要转换为数组
const arr = [...map];
console.log(JSON.stringify(arr)); // '[["name","Alice"]]'
const uniqueIds = new Set([1, 2, 2, 3, 3]);
const userMap = new Map();
userMap.set(userObject, userData);
const elementData = new WeakMap();
elementData.set(domElement, data);
const cache = new Map(); // 而不是 {}