Skip to content

ES6新增数据结构2024:Set和Map数据结构完整实战指南

📊 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新增数据结构学习目标与核心收获

通过本节ES6新增数据结构教程,你将系统性掌握:

  • Set数据结构基本用法:掌握Set的创建、操作和遍历方法
  • Set实现数组去重:学会使用Set高效解决数组去重问题
  • Map vs Object区别:深入理解Map和Object的差异和适用场景
  • Map的应用场景:掌握Map在实际开发中的各种应用
  • WeakSet和WeakMap:了解弱引用集合的特点和使用场景
  • 性能优化应用:学会在合适场景下选择最优的数据结构

🎯 适合人群

  • JavaScript中级开发者的ES6数据结构深入学习和性能优化
  • 前端工程师的数据处理和算法优化技能提升
  • 全栈开发者的数据结构选择和内存管理优化
  • 算法学习者的JavaScript数据结构基础建设

🌟 为什么需要新的数据结构?Set和Map如何提升开发效率?

为什么需要新的数据结构?这是现代JavaScript数据处理的核心问题。传统的Array和Object虽然强大,但在某些特定场景下存在局限性,而Set和Map提供了更专业、更高效的解决方案,也是ES6+现代JavaScript的重要补充。

新增数据结构的核心价值

  • 🎯 专业化设计:针对特定使用场景设计的专业数据结构
  • 🔧 性能优化:在特定操作上提供更好的性能表现
  • 💡 语义清晰:让代码意图更加明确和易懂
  • 📚 功能增强:提供传统数据结构不具备的特殊功能
  • 🚀 标准化:提供跨平台一致的数据结构实现

💡 核心原则:选择最适合的数据结构,让代码更高效、更清晰、更易维护

Set数据结构

Set是一种集合数据结构,存储唯一值的集合,是数组去重和集合运算的理想选择。

javascript
// 🎉 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']));

Set的核心特点

  • 唯一性:自动去除重复值
  • 任意类型:可以存储任意类型的值
  • 大小获取:通过size属性获取元素数量
  • 高效查找:has()方法的时间复杂度为O(1)

Map数据结构

Map是一种键值对集合,相比Object提供了更强大和灵活的键值对存储能力。

javascript
// 🎉 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', '李四');

Map vs Object的关键差异

  • 键类型:Map支持任意类型的键,Object只支持字符串和Symbol
  • 大小获取:Map有size属性,Object需要Object.keys().length
  • 遍历顺序:Map保证插入顺序,Object在某些情况下不保证
  • 性能:Map在频繁增删操作时性能更好

WeakSet和WeakMap

WeakSet和WeakMap是弱引用版本的Set和Map,具有特殊的垃圾回收特性。

javascript
// 🎉 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元素被移除时,相关数据也会被自动清理

WeakSet和WeakMap的特点

  • 弱引用:不阻止垃圾回收器回收键对象
  • 只存储对象:键必须是对象,不能是原始值
  • 不可遍历:没有size属性,不能遍历
  • 自动清理:当键对象被回收时,相关条目自动清除

📚 ES6新增数据结构学习总结与下一步规划

✅ 本节核心收获回顾

通过本节ES6新增数据结构教程的学习,你已经掌握:

  1. Set数据结构:掌握了Set的基本操作、数组去重、集合运算等应用
  2. Map数据结构:理解了Map与Object的差异,掌握了Map的各种应用场景
  3. 实际应用场景:学会了在权限管理、缓存系统、事件管理中的应用
  4. WeakSet和WeakMap:了解了弱引用集合的特点和私有数据存储等用途
  5. 性能优化考虑:学会了在不同场景下选择最适合的数据结构

🎯 数据结构下一步

  1. 高级数据结构:学习更复杂的数据结构如树、图等的JavaScript实现
  2. 算法应用:将新数据结构应用到具体的算法问题中
  3. 性能分析:深入分析不同数据结构的时间和空间复杂度
  4. 实际项目应用:在真实项目中应用这些数据结构优化性能

🔗 相关学习资源

  • 数据结构与算法:系统学习计算机科学中的数据结构知识
  • JavaScript性能优化:深入学习JavaScript性能优化技术
  • 函数式编程:学习如何在函数式编程中使用这些数据结构
  • 设计模式:了解数据结构在设计模式中的应用

💪 实践练习建议

  1. 数据结构重构:将现有项目中的数组和对象操作优化为使用Set和Map
  2. 算法实现:使用新数据结构实现经典算法(如图的遍历等)
  3. 性能测试:对比不同数据结构在具体场景下的性能表现
  4. 工具库开发:开发基于新数据结构的通用工具库

🔍 常见问题FAQ

Q1: 什么时候应该使用Set而不是Array?

A: 当需要存储唯一值、频繁检查元素存在性、进行集合运算时,使用Set更合适。Array适合需要索引访问、保持重复元素的场景。

Q2: Map和Object的性能差异如何?

A: Map在频繁增删操作时性能更好,Object在属性访问时可能更快。当键不是字符串或需要保持插入顺序时,应该使用Map。

Q3: WeakMap的主要用途是什么?

A: WeakMap主要用于存储对象的私有数据、DOM元素的关联数据、避免内存泄漏的场景。它的弱引用特性使得相关数据会随着对象的回收而自动清理。

Q4: Set和Map的遍历性能如何?

A: Set和Map的遍历性能都很好,且保证按插入顺序遍历。对于大量数据的遍历,它们通常比Array和Object更高效。

Q5: 如何选择合适的数据结构?

A: 根据具体需求选择:需要唯一值用Set,需要键值对且键类型多样用Map,需要弱引用用WeakSet/WeakMap,其他情况考虑传统的Array/Object。


"掌握ES6新增的数据结构是现代JavaScript开发的重要技能。Set和Map不仅提供了更专业的数据存储方案,还能在特定场景下显著提升性能。在实际开发中合理选择数据结构,让你的代码更高效、更优雅!"