Search K
Appearance
Appearance
📊 SEO元描述:2024年最新ES6新增数据结构教程,详解Set数据结构基本用法、数组去重、Map vs Object区别、WeakSet和WeakMap。包含完整实战案例,适合JavaScript开发者掌握现代数据结构。
核心关键词:ES6新增数据结构2024、Set数据结构、Map数据结构、数组去重、WeakSet WeakMap、JavaScript集合类型
长尾关键词:Set怎么用、Map和Object区别、JavaScript数组去重方法、Set Map实际应用、WeakSet WeakMap使用场景
通过本节ES6新增数据结构教程,你将系统性掌握:
为什么需要新的数据结构?这是现代JavaScript数据处理的核心问题。传统的Array和Object虽然强大,但在某些特定场景下存在局限性,而Set和Map提供了更专业、更高效的解决方案,也是ES6+现代JavaScript的重要补充。
💡 核心原则:选择最适合的数据结构,让代码更高效、更清晰、更易维护
Set是一种集合数据结构,存储唯一值的集合,是数组去重和集合运算的理想选择。
// 🎉 Set数据结构详解
console.log('=== Set基本操作 ===');
// 1. Set的创建
const emptySet = new Set();
const setFromArray = new Set([1, 2, 3, 3, 4]); // 自动去重
const setFromString = new Set('hello'); // 字符去重
const setFromIterable = new Set(document.querySelectorAll('div')); // 从可迭代对象创建
console.log('空Set:', emptySet);
console.log('从数组创建:', setFromArray); // Set(4) {1, 2, 3, 4}
console.log('从字符串创建:', setFromString); // Set(4) {'h', 'e', 'l', 'o'}
// 2. Set的基本方法
const mySet = new Set();
// 添加元素
mySet.add(1);
mySet.add(2);
mySet.add(2); // 重复添加无效
mySet.add('hello');
mySet.add({ name: 'object' });
console.log('添加后的Set:', mySet);
console.log('Set大小:', mySet.size);
// 检查元素存在
console.log('是否包含1:', mySet.has(1)); // true
console.log('是否包含3:', mySet.has(3)); // false
// 删除元素
console.log('删除2:', mySet.delete(2)); // true
console.log('删除不存在的元素:', mySet.delete(999)); // false
console.log('删除后的Set:', mySet);
// 清空Set
const tempSet = new Set([1, 2, 3]);
tempSet.clear();
console.log('清空后的Set:', tempSet);
// 3. Set的遍历方法
const iterableSet = new Set(['apple', 'banana', 'cherry']);
console.log('\n=== Set遍历方法 ===');
// forEach遍历
console.log('forEach遍历:');
iterableSet.forEach((value, key, set) => {
console.log(`值: ${value}, 键: ${key}`); // 在Set中,key和value相同
});
// for...of遍历
console.log('for...of遍历:');
for (const value of iterableSet) {
console.log('值:', value);
}
// 迭代器方法
console.log('keys():', [...iterableSet.keys()]);
console.log('values():', [...iterableSet.values()]);
console.log('entries():', [...iterableSet.entries()]);
// 4. Set实现数组去重
console.log('\n=== 数组去重应用 ===');
const arrayWithDuplicates = [1, 2, 2, 3, 3, 3, 4, 5, 5];
console.log('原数组:', arrayWithDuplicates);
// 方法1:使用Set + 扩展运算符
const uniqueArray1 = [...new Set(arrayWithDuplicates)];
console.log('去重方法1:', uniqueArray1);
// 方法2:使用Array.from
const uniqueArray2 = Array.from(new Set(arrayWithDuplicates));
console.log('去重方法2:', uniqueArray2);
// 复杂数据去重
const complexArray = [
{ id: 1, name: 'A' },
{ id: 2, name: 'B' },
{ id: 1, name: 'A' }, // 重复
{ id: 3, name: 'C' }
];
// 基于属性去重
function uniqueByProperty(array, property) {
const seen = new Set();
return array.filter(item => {
const value = item[property];
if (seen.has(value)) {
return false;
}
seen.add(value);
return true;
});
}
const uniqueComplex = uniqueByProperty(complexArray, 'id');
console.log('复杂对象去重:', uniqueComplex);
// 5. Set的集合运算
console.log('\n=== 集合运算 ===');
const setA = new Set([1, 2, 3, 4]);
const setB = new Set([3, 4, 5, 6]);
// 并集
const union = new Set([...setA, ...setB]);
console.log('并集:', union);
// 交集
const intersection = new Set([...setA].filter(x => setB.has(x)));
console.log('交集:', intersection);
// 差集
const difference = new Set([...setA].filter(x => !setB.has(x)));
console.log('差集:', difference);
// 对称差集
const symmetricDifference = new Set([
...[...setA].filter(x => !setB.has(x)),
...[...setB].filter(x => !setA.has(x))
]);
console.log('对称差集:', symmetricDifference);
// 6. 实际应用:权限管理
class PermissionManager {
constructor() {
this.permissions = new Set();
}
addPermission(permission) {
this.permissions.add(permission);
console.log(`添加权限: ${permission}`);
}
removePermission(permission) {
const removed = this.permissions.delete(permission);
console.log(`删除权限 ${permission}: ${removed ? '成功' : '失败'}`);
}
hasPermission(permission) {
return this.permissions.has(permission);
}
getAllPermissions() {
return [...this.permissions];
}
hasAllPermissions(requiredPermissions) {
return requiredPermissions.every(permission =>
this.permissions.has(permission)
);
}
}
const userPermissions = new PermissionManager();
userPermissions.addPermission('read');
userPermissions.addPermission('write');
userPermissions.addPermission('delete');
console.log('用户权限:', userPermissions.getAllPermissions());
console.log('是否有读权限:', userPermissions.hasPermission('read'));
console.log('是否有所有权限:', userPermissions.hasAllPermissions(['read', 'write']));Map是一种键值对集合,相比Object提供了更强大和灵活的键值对存储能力。
// 🎉 Map数据结构详解
console.log('=== Map基本操作 ===');
// 1. Map的创建
const emptyMap = new Map();
const mapFromArray = new Map([
['name', '张三'],
['age', 25],
['city', '北京']
]);
const mapFromIterable = new Map(Object.entries({ a: 1, b: 2 }));
console.log('空Map:', emptyMap);
console.log('从数组创建:', mapFromArray);
console.log('从对象创建:', mapFromIterable);
// 2. Map的基本方法
const userMap = new Map();
// 设置键值对
userMap.set('id', 1);
userMap.set('name', '李四');
userMap.set('email', 'lisi@example.com');
userMap.set(123, '数字键'); // 数字作为键
userMap.set(true, '布尔键'); // 布尔值作为键
userMap.set({}, '对象键'); // 对象作为键
console.log('设置后的Map:', userMap);
console.log('Map大小:', userMap.size);
// 获取值
console.log('获取name:', userMap.get('name'));
console.log('获取数字键:', userMap.get(123));
console.log('获取不存在的键:', userMap.get('nonexistent')); // undefined
// 检查键存在
console.log('是否有name键:', userMap.has('name')); // true
console.log('是否有phone键:', userMap.has('phone')); // false
// 删除键值对
console.log('删除email:', userMap.delete('email')); // true
console.log('删除不存在的键:', userMap.delete('nonexistent')); // false
// 清空Map
const tempMap = new Map([['a', 1], ['b', 2]]);
tempMap.clear();
console.log('清空后的Map:', tempMap);
// 3. Map vs Object对比
console.log('\n=== Map vs Object对比 ===');
// Object的限制
const obj = {};
obj['name'] = '王五';
obj[1] = '数字键会被转为字符串';
obj[true] = '布尔值会被转为字符串';
console.log('Object键:', Object.keys(obj)); // ['1', 'name', 'true']
// Map的优势
const map = new Map();
map.set('name', '王五');
map.set(1, '真正的数字键');
map.set(true, '真正的布尔键');
map.set({id: 1}, '对象键');
map.set(function() {}, '函数键');
console.log('Map键类型:');
for (const [key, value] of map) {
console.log(`键: ${key} (${typeof key}), 值: ${value}`);
}
// 4. Map的遍历方法
const iterableMap = new Map([
['apple', 100],
['banana', 200],
['cherry', 300]
]);
console.log('\n=== Map遍历方法 ===');
// forEach遍历
console.log('forEach遍历:');
iterableMap.forEach((value, key, map) => {
console.log(`${key}: ${value}`);
});
// for...of遍历
console.log('for...of遍历:');
for (const [key, value] of iterableMap) {
console.log(`${key} => ${value}`);
}
// 迭代器方法
console.log('keys():', [...iterableMap.keys()]);
console.log('values():', [...iterableMap.values()]);
console.log('entries():', [...iterableMap.entries()]);
// 5. 实际应用:缓存系统
class SimpleCache {
constructor(maxSize = 100) {
this.cache = new Map();
this.maxSize = maxSize;
}
get(key) {
if (this.cache.has(key)) {
// LRU策略:将访问的项移到最后
const value = this.cache.get(key);
this.cache.delete(key);
this.cache.set(key, value);
return value;
}
return null;
}
set(key, value) {
if (this.cache.has(key)) {
this.cache.delete(key);
} else if (this.cache.size >= this.maxSize) {
// 删除最旧的项(第一个)
const firstKey = this.cache.keys().next().value;
this.cache.delete(firstKey);
}
this.cache.set(key, value);
}
has(key) {
return this.cache.has(key);
}
clear() {
this.cache.clear();
}
size() {
return this.cache.size;
}
getStats() {
return {
size: this.cache.size,
maxSize: this.maxSize,
keys: [...this.cache.keys()]
};
}
}
const cache = new SimpleCache(3);
cache.set('user:1', { name: '用户1' });
cache.set('user:2', { name: '用户2' });
cache.set('user:3', { name: '用户3' });
console.log('缓存状态:', cache.getStats());
cache.set('user:4', { name: '用户4' }); // 会删除最旧的user:1
console.log('添加新项后:', cache.getStats());
console.log('获取user:2:', cache.get('user:2'));
console.log('访问后的状态:', cache.getStats()); // user:2被移到最后
// 6. 实际应用:事件监听器管理
class EventManager {
constructor() {
this.listeners = new Map();
}
on(event, callback) {
if (!this.listeners.has(event)) {
this.listeners.set(event, new Set());
}
this.listeners.get(event).add(callback);
}
off(event, callback) {
if (this.listeners.has(event)) {
this.listeners.get(event).delete(callback);
if (this.listeners.get(event).size === 0) {
this.listeners.delete(event);
}
}
}
emit(event, ...args) {
if (this.listeners.has(event)) {
this.listeners.get(event).forEach(callback => {
callback(...args);
});
}
}
getEventStats() {
const stats = {};
for (const [event, callbacks] of this.listeners) {
stats[event] = callbacks.size;
}
return stats;
}
}
const eventManager = new EventManager();
const loginHandler = (user) => console.log(`用户登录: ${user}`);
const logoutHandler = (user) => console.log(`用户登出: ${user}`);
eventManager.on('login', loginHandler);
eventManager.on('logout', logoutHandler);
eventManager.on('login', (user) => console.log(`记录登录日志: ${user}`));
console.log('事件统计:', eventManager.getEventStats());
eventManager.emit('login', '张三');
eventManager.emit('logout', '李四');WeakSet和WeakMap是弱引用版本的Set和Map,具有特殊的垃圾回收特性。
// 🎉 WeakSet和WeakMap详解
console.log('=== WeakSet应用 ===');
// 1. WeakSet基本特点
const weakSet = new WeakSet();
// 只能存储对象
const obj1 = { id: 1 };
const obj2 = { id: 2 };
weakSet.add(obj1);
weakSet.add(obj2);
// weakSet.add(1); // TypeError: 只能添加对象
console.log('WeakSet包含obj1:', weakSet.has(obj1)); // true
console.log('WeakSet包含obj2:', weakSet.has(obj2)); // true
// 删除对象
weakSet.delete(obj1);
console.log('删除后包含obj1:', weakSet.has(obj1)); // false
// 2. WeakSet实际应用:对象标记
class ObjectTracker {
constructor() {
this.trackedObjects = new WeakSet();
}
track(obj) {
this.trackedObjects.add(obj);
console.log('开始跟踪对象');
}
untrack(obj) {
this.trackedObjects.delete(obj);
console.log('停止跟踪对象');
}
isTracked(obj) {
return this.trackedObjects.has(obj);
}
}
const tracker = new ObjectTracker();
const targetObj = { name: '目标对象' };
tracker.track(targetObj);
console.log('对象是否被跟踪:', tracker.isTracked(targetObj));
// 当targetObj被垃圾回收时,WeakSet中的引用也会自动清除
console.log('\n=== WeakMap应用 ===');
// 3. WeakMap基本特点
const weakMap = new WeakMap();
const key1 = { id: 1 };
const key2 = { id: 2 };
weakMap.set(key1, '值1');
weakMap.set(key2, '值2');
console.log('WeakMap获取key1:', weakMap.get(key1)); // '值1'
console.log('WeakMap是否有key2:', weakMap.has(key2)); // true
// 4. WeakMap实际应用:私有数据存储
const privateData = new WeakMap();
class User {
constructor(name, email) {
// 使用WeakMap存储私有数据
privateData.set(this, {
name,
email,
createdAt: new Date()
});
}
getName() {
return privateData.get(this).name;
}
getEmail() {
return privateData.get(this).email;
}
updateEmail(newEmail) {
const data = privateData.get(this);
data.email = newEmail;
data.updatedAt = new Date();
}
getPrivateInfo() {
return privateData.get(this);
}
}
const user = new User('赵六', 'zhaoliu@example.com');
console.log('用户名:', user.getName());
console.log('邮箱:', user.getEmail());
user.updateEmail('new.email@example.com');
console.log('更新后的私有信息:', user.getPrivateInfo());
// 5. WeakMap应用:DOM元素数据关联
const elementData = new WeakMap();
function attachData(element, data) {
elementData.set(element, data);
}
function getData(element) {
return elementData.get(element);
}
function removeData(element) {
elementData.delete(element);
}
// 模拟DOM元素
const mockElement = { tagName: 'DIV', id: 'test' };
attachData(mockElement, {
clickCount: 0,
lastClicked: null
});
console.log('元素数据:', getData(mockElement));
// 当DOM元素被移除时,相关数据也会被自动清理通过本节ES6新增数据结构教程的学习,你已经掌握:
A: 当需要存储唯一值、频繁检查元素存在性、进行集合运算时,使用Set更合适。Array适合需要索引访问、保持重复元素的场景。
A: Map在频繁增删操作时性能更好,Object在属性访问时可能更快。当键不是字符串或需要保持插入顺序时,应该使用Map。
A: WeakMap主要用于存储对象的私有数据、DOM元素的关联数据、避免内存泄漏的场景。它的弱引用特性使得相关数据会随着对象的回收而自动清理。
A: Set和Map的遍历性能都很好,且保证按插入顺序遍历。对于大量数据的遍历,它们通常比Array和Object更高效。
A: 根据具体需求选择:需要唯一值用Set,需要键值对且键类型多样用Map,需要弱引用用WeakSet/WeakMap,其他情况考虑传统的Array/Object。
"掌握ES6新增的数据结构是现代JavaScript开发的重要技能。Set和Map不仅提供了更专业的数据存储方案,还能在特定场景下显著提升性能。在实际开发中合理选择数据结构,让你的代码更高效、更优雅!"