Set是值的集合,值唯一。
// 创建空Set
const set1 = new Set();
// 从数组创建
const set2 = new Set([1, 2, 3, 2, 1]);
console.log(set2); // Set { 1, 2, 3 }
// 从字符串创建
const set3 = new Set("hello");
console.log(set3); // Set { "h", "e", "l", "o" }
const set = new Set();
// 添加值
set.add(1);
set.add(2);
set.add(2); // 重复值会被忽略
console.log(set.size); // 2
// 链式添加
set.add(3).add(4).add(5);
// 检查是否存在
console.log(set.has(1)); // true
console.log(set.has(6)); // false
// 删除值
set.delete(3);
console.log(set.has(3)); // false
// 清空
// set.clear();
// 转换为数组
const arr = [...set];
console.log(arr); // [1, 2, 4, 5]
// 或使用Array.from
const arr2 = Array.from(set);
const set = new Set(["东巴文", "db-w.cn"]);
// for...of
for (const value of set) {
console.log(value);
}
// forEach
set.forEach((value, key, set) => {
console.log(value); // Set中key和value相同
});
// keys(), values(), entries()
console.log([...set.keys()]); // ["东巴文", "db-w.cn"]
console.log([...set.values()]); // ["东巴文", "db-w.cn"]
console.log([...set.entries()]); // [["东巴文", "东巴文"], ["db-w.cn", "db-w.cn"]]
Set的实用方法。
// 数组去重
const arr = [1, 2, 2, 3, 3, 3];
const unique = [...new Set(arr)];
console.log(unique); // [1, 2, 3]
// 对象数组去重
const users = [
{ id: 1, name: "东巴文" },
{ id: 2, name: "db-w.cn" },
{ id: 1, name: "东巴文" }
];
const uniqueUsers = [...new Map(users.map(u => [u.id, u])).values()];
console.log(uniqueUsers);
const a = new Set([1, 2, 3]);
const b = new Set([2, 3, 4]);
// 并集
const union = new Set([...a, ...b]);
console.log([...union]); // [1, 2, 3, 4]
// 交集
const intersection = new Set([...a].filter(x => b.has(x)));
console.log([...intersection]); // [2, 3]
// 差集
const difference = new Set([...a].filter(x => !b.has(x)));
console.log([...difference]); // [1]
// 对称差集
const symmetricDiff = new Set([
...[...a].filter(x => !b.has(x)),
...[...b].filter(x => !a.has(x))
]);
console.log([...symmetricDiff]); // [1, 4]
// 判断是否是子集
function isSubset(subset, superset) {
for (const value of subset) {
if (!superset.has(value)) {
return false;
}
}
return true;
}
const a = new Set([1, 2]);
const b = new Set([1, 2, 3, 4]);
console.log(isSubset(a, b)); // true
console.log(isSubset(b, a)); // false
WeakSet是弱引用的Set。
// WeakSet只能存储对象
const weakSet = new WeakSet();
const obj1 = { name: "东巴文" };
const obj2 = { name: "db-w.cn" };
weakSet.add(obj1);
weakSet.add(obj2);
console.log(weakSet.has(obj1)); // true
// 不能存储原始值
// weakSet.add(1); // TypeError
// 特点:
// 1. 只能存储对象
// 2. 弱引用,不影响垃圾回收
// 3. 不可遍历
// 4. 没有size属性
// 存储DOM元素引用
const disabledElements = new WeakSet();
function disable(element) {
disabledElements.add(element);
element.disabled = true;
}
function enable(element) {
disabledElements.delete(element);
element.disabled = false;
}
function isDisabled(element) {
return disabledElements.has(element);
}
// 对象被删除后,WeakSet中的引用自动清除
Map是键值对集合。
// 创建空Map
const map1 = new Map();
// 从数组创建
const map2 = new Map([
["name", "东巴文"],
["age", 25]
]);
console.log(map2.get("name")); // 东巴文
// 从对象创建
const obj = { x: 1, y: 2 };
const map3 = new Map(Object.entries(obj));
console.log(map3.get("x")); // 1
const map = new Map();
// 设置键值对
map.set("name", "东巴文");
map.set("age", 25);
// 链式设置
map.set("email", "info@db-w.cn").set("url", "db-w.cn");
// 获取值
console.log(map.get("name")); // 东巴文
console.log(map.get("unknown")); // undefined
// 检查键是否存在
console.log(map.has("name")); // true
console.log(map.has("x")); // false
// 获取大小
console.log(map.size); // 4
// 删除
map.delete("age");
console.log(map.has("age")); // false
// 清空
// map.clear();
const map = new Map();
// 各种类型的键
map.set("string", "字符串键");
map.set(123, "数字键");
map.set(true, "布尔键");
map.set(undefined, "undefined键");
map.set(null, "null键");
map.set({ id: 1 }, "对象键");
map.set([1, 2], "数组键");
map.set(() => {}, "函数键");
map.set(NaN, "NaN键");
// NaN作为键
map.set(NaN, "NaN");
console.log(map.get(NaN)); // "NaN"
console.log(map.has(NaN)); // true
// 对象键是引用比较
const obj1 = { id: 1 };
const obj2 = { id: 1 };
map.set(obj1, "对象1");
map.set(obj2, "对象2");
console.log(map.get(obj1)); // "对象1"
console.log(map.get(obj2)); // "对象2"
Map的遍历和转换方法。
const map = new Map([
["name", "东巴文"],
["age", 25],
["url", "db-w.cn"]
]);
// for...of
for (const [key, value] of map) {
console.log(key, value);
}
// forEach
map.forEach((value, key, map) => {
console.log(key, value);
});
// keys()
for (const key of map.keys()) {
console.log(key);
}
// values()
for (const value of map.values()) {
console.log(value);
}
// entries()
for (const [key, value] of map.entries()) {
console.log(key, value);
}
const map = new Map([
["name", "东巴文"],
["age", 25]
]);
// 转换为数组
const arr = [...map];
console.log(arr); // [["name", "东巴文"], ["age", 25]]
// 转换为对象
const obj = Object.fromEntries(map);
console.log(obj); // { name: "东巴文", age: 25 }
// 对象转Map
const obj2 = { x: 1, y: 2 };
const map2 = new Map(Object.entries(obj2));
console.log(map2.get("x")); // 1
Map和Object的区别。
// 1. 键的类型
// Object: 只能是字符串或Symbol
// Map: 任意类型
// 2. 键的顺序
// Object: 无序(整数键排序在前)
// Map: 插入顺序
// 3. 大小
// Object: 需要手动计算
// Map: size属性
const obj = {};
obj[2] = "two";
obj[1] = "one";
obj["a"] = "a";
console.log(Object.keys(obj)); // ["1", "2", "a"]
const map = new Map();
map.set(2, "two");
map.set(1, "one");
map.set("a", "a");
console.log([...map.keys()]); // [2, 1, "a"]
// 4. 原型链
// Object: 有原型链,可能有默认键
// Map: 干净的键值对
console.log("toString" in {}); // true
console.log("toString" in Object.create(null)); // false
// 使用Map的场景:
// 1. 键的类型不确定
// 2. 需要保持插入顺序
// 3. 频繁增删键值对
// 4. 需要知道大小
// 使用Object的场景:
// 1. 存储记录/实体
// 2. 需要JSON序列化
// 3. 键是字符串或Symbol
// 4. 作为配置对象
// 性能对比
const map = new Map();
const obj = {};
// 大量操作时Map更快
console.time("Map");
for (let i = 0; i < 100000; i++) {
map.set(i, i);
}
console.timeEnd("Map");
console.time("Object");
for (let i = 0; i < 100000; i++) {
obj[i] = i;
}
console.timeEnd("Object");
WeakMap是弱引用的Map。
// WeakMap的键必须是对象
const weakMap = new WeakMap();
const obj = { name: "东巴文" };
weakMap.set(obj, "值");
console.log(weakMap.get(obj)); // "值"
// 特点:
// 1. 键必须是对象
// 2. 弱引用键,不影响垃圾回收
// 3. 不可遍历
// 4. 没有size属性
// 5. 没有keys(), values(), entries()
// 存储私有数据
const privateData = new WeakMap();
class Person {
constructor(name, age) {
privateData.set(this, { name, age });
}
get name() {
return privateData.get(this).name;
}
get age() {
return privateData.get(this).age;
}
}
const person = new Person("东巴文", 25);
console.log(person.name); // 东巴文
// 缓存计算结果
const cache = new WeakMap();
function compute(obj) {
if (cache.has(obj)) {
return cache.get(obj);
}
const result = expensiveCalculation(obj);
cache.set(obj, result);
return result;
}
// DOM关联数据
const elementData = new WeakMap();
function setData(element, key, value) {
if (!elementData.has(element)) {
elementData.set(element, new Map());
}
elementData.get(element).set(key, value);
}
function getData(element, key) {
return elementData.get(element)?.get(key);
}
掌握了Map与Set后,让我们继续学习:
东巴文(db-w.cn) - 让编程学习更简单
🎯 东巴文寄语:Map和Set是ES6新增的集合类型,相比Object和Array,它们提供了更灵活的键值存储和更高效的值唯一性保证。在 db-w.cn,我们帮你掌握现代JavaScript数据结构!