JavaScript Set 和 Map:现代 JavaScript 的数据结构双雄

在 JavaScript 的世界里,数组(Array)和对象(Object)一直是我们处理数据的主力军。但随着 ES6 的到来,两个新的数据结构悄然登场:Set 和 Map。它们不仅解决了传统数据结构的一些痛点,还为我们提供了更优雅、更高效的解决方案。

今天,我们就来深入探讨这两个现代 JavaScript 的数据结构明星。

什么是 Set?

Set 就像是一个"绝不允许重复"的数组。它是一个集合,存储唯一值的集合,任何重复的值都会被自动忽略。

javascript 复制代码
// 传统数组允许重复
const numbers = [1, 2, 2, 3, 3, 3];
console.log(numbers); // [1, 2, 2, 3, 3, 3]

// Set 自动去重
const uniqueNumbers = new Set([1, 2, 2, 3, 3, 3]);
console.log(uniqueNumbers); // Set(3) {1, 2, 3}

什么是 Map?

Map 就像是一个"超级对象"。与普通对象只能使用字符串(和 Symbol)作为键不同,Map 可以使用任何类型的值作为键,包括对象、函数,甚至是其他 Map。

javascript 复制代码
// 传统对象的限制
const obj = {};
obj[1] = 'number key';
obj['1'] = 'string key';
console.log(obj); // {'1': 'string key'} - 数字键被转换为字符串!

// Map 的灵活性
const map = new Map();
map.set(1, 'number key');
map.set('1', 'string key');
map.set(true, 'boolean key');
console.log(map.get(1));    // 'number key'
console.log(map.get('1'));  // 'string key'
console.log(map.get(true)); // 'boolean key'

Set 的深入探索

基础操作

javascript 复制代码
// 创建 Set
const mySet = new Set();

// 添加元素
mySet.add(1);
mySet.add(2);
mySet.add(2); // 重复值会被忽略
mySet.add('hello');

console.log(mySet); // Set(3) {1, 2, 'hello'}
console.log(mySet.size); // 3

// 检查元素是否存在
console.log(mySet.has(1));     // true
console.log(mySet.has('hi'));  // false

// 删除元素
mySet.delete(2);
console.log(mySet); // Set(2) {1, 'hello'}

// 清空 Set
mySet.clear();
console.log(mySet); // Set(0) {}

实战场景1:数组去重

javascript 复制代码
// 传统方法:使用 filter + indexOf
function removeDuplicatesOld(arr) {
    return arr.filter((item, index) => arr.indexOf(item) === index);
}

const numbers = [1, 2, 2, 3, 4, 4, 5];
console.time('传统方法');
const result1 = removeDuplicatesOld(numbers);
console.timeEnd('传统方法');
console.log(result1); // [1, 2, 3, 4, 5]

// Set 方法:简洁高效
console.time('Set 方法');
const result2 = [...new Set(numbers)];
console.timeEnd('Set 方法');
console.log(result2); // [1, 2, 3, 4, 5]

// 处理复杂数据
const users = [
    { id: 1, name: 'Alice' },
    { id: 2, name: 'Bob' },
    { id: 1, name: 'Alice' },
    { id: 3, name: 'Charlie' }
];

// Set 对对象的处理(注意:对象引用比较)
const userSet = new Set(users);
console.log(userSet.size); // 4 - 因为是不同的对象引用

// 基于属性去重的正确方法
function uniqueById(arr) {
    const seen = new Set();
    return arr.filter(user => {
        if (seen.has(user.id)) {
            return false;
        }
        seen.add(user.id);
        return true;
    });
}

console.log(uniqueById(users)); // [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }, { id: 3, name: 'Charlie' }]

实战场景2:集合运算

javascript 复制代码
// 使用 Set 实现数学集合运算
const setA = new Set([1, 2, 3, 4]);
const setB = new Set([3, 4, 5, 6]);

// 并集 (Union)
const union = new Set([...setA, ...setB]);
console.log('并集:', [...union]); // [1, 2, 3, 4, 5, 6]

// 交集 (Intersection)
const intersection = new Set([...setA].filter(x => setB.has(x)));
console.log('交集:', [...intersection]); // [3, 4]

// 差集 (Difference)
const difference = new Set([...setA].filter(x => !setB.has(x)));
console.log('差集:', [...difference]); // [1, 2]

// 封装为工具函数
class SetOperations {
    static union(setA, setB) {
        return new Set([...setA, ...setB]);
    }
    
    static intersection(setA, setB) {
        return new Set([...setA].filter(x => setB.has(x)));
    }
    
    static difference(setA, setB) {
        return new Set([...setA].filter(x => !setB.has(x)));
    }
    
    static symmetricDifference(setA, setB) {
        return new Set([
            ...[...setA].filter(x => !setB.has(x)),
            ...[...setB].filter(x => !setA.has(x))
        ]);
    }
}

// 实际应用:用户权限管理
const adminPermissions = new Set(['read', 'write', 'delete', 'admin']);
const userPermissions = new Set(['read', 'write']);
const guestPermissions = new Set(['read']);

console.log('管理员独有权限:', 
    [...SetOperations.difference(adminPermissions, userPermissions)]
); // ['delete', 'admin']

console.log('用户和访客共同权限:', 
    [...SetOperations.intersection(userPermissions, guestPermissions)]
); // ['read']

实战场景3:性能优化的查找

javascript 复制代码
// 场景:检查大量数据中是否存在某个值
const largeArray = Array.from({length: 100000}, (_, i) => i);
const targetValues = [50000, 75000, 99999, 100001];

// 传统方法:使用 includes
console.time('Array includes');
targetValues.forEach(target => {
    const found = largeArray.includes(target);
    // console.log(`${target}: ${found}`);
});
console.timeEnd('Array includes'); // 较慢

// Set 方法:O(1) 查找时间
const largeSet = new Set(largeArray);
console.time('Set has');
targetValues.forEach(target => {
    const found = largeSet.has(target);
    // console.log(`${target}: ${found}`);
});
console.timeEnd('Set has'); // 更快

// 实际应用:黑名单检查
class BlacklistChecker {
    constructor(blacklist) {
        this.blacklistSet = new Set(blacklist);
    }
    
    isBlocked(item) {
        return this.blacklistSet.has(item);
    }
    
    addToBlacklist(item) {
        this.blacklistSet.add(item);
    }
    
    removeFromBlacklist(item) {
        this.blacklistSet.delete(item);
    }
}

const checker = new BlacklistChecker(['spam@email.com', 'bad-user', '192.168.1.100']);
console.log(checker.isBlocked('spam@email.com')); // true
console.log(checker.isBlocked('good@email.com')); // false

Map 的深入探索

基础操作

javascript 复制代码
// 创建 Map
const myMap = new Map();

// 设置键值对
myMap.set('name', 'Alice');
myMap.set(42, 'number key');
myMap.set(true, 'boolean key');

// 链式调用
myMap.set('age', 25).set('city', 'New York');

console.log(myMap.size); // 5

// 获取值
console.log(myMap.get('name')); // 'Alice'
console.log(myMap.get(42));     // 'number key'
console.log(myMap.get('missing')); // undefined

// 检查键是否存在
console.log(myMap.has('name')); // true
console.log(myMap.has('email')); // false

// 删除键值对
myMap.delete(42);
console.log(myMap.has(42)); // false

// 清空 Map
// myMap.clear();

Map vs Object:关键区别

javascript 复制代码
// 1. 键的类型限制
const obj = {};
const map = new Map();

// Object:键会被转换为字符串
obj[1] = 'one';
obj['1'] = 'string one';
console.log(obj); // {'1': 'string one'} - 数字键被覆盖了!

// Map:保持键的原始类型
map.set(1, 'one');
map.set('1', 'string one');
console.log(map.get(1));   // 'one'
console.log(map.get('1')); // 'string one'

// 2. 大小获取
console.log('Object size:', Object.keys(obj).length); // 需要计算
console.log('Map size:', map.size); // 直接属性

// 3. 迭代方式
const dataObj = { a: 1, b: 2, c: 3 };
const dataMap = new Map([['a', 1], ['b', 2], ['c', 3]]);

// Object 迭代
console.log('Object keys:', Object.keys(dataObj));
console.log('Object values:', Object.values(dataObj));
console.log('Object entries:', Object.entries(dataObj));

// Map 迭代(更直接)
console.log('Map keys:', [...dataMap.keys()]);
console.log('Map values:', [...dataMap.values()]);
console.log('Map entries:', [...dataMap.entries()]);

// 4. 性能对比
console.time('Object creation');
const objTest = {};
for (let i = 0; i < 100000; i++) {
    objTest[i] = i;
}
console.timeEnd('Object creation');

console.time('Map creation');
const mapTest = new Map();
for (let i = 0; i < 100000; i++) {
    mapTest.set(i, i);
}
console.timeEnd('Map creation');

实战场景1:缓存系统

javascript 复制代码
// 传统对象缓存的问题
class OldCache {
    constructor() {
        this.cache = {};
    }
    
    set(key, value) {
        // 问题:所有键都会被转换为字符串
        this.cache[key] = value;
    }
    
    get(key) {
        return this.cache[key];
    }
    
    has(key) {
        return key in this.cache;
    }
    
    clear() {
        this.cache = {};
    }
}

// Map 实现的现代缓存
class ModernCache {
    constructor(maxSize = 100) {
        this.cache = new Map();
        this.maxSize = maxSize;
    }
    
    set(key, value) {
        // 如果缓存已满,删除最旧的条目
        if (this.cache.size >= this.maxSize) {
            const firstKey = this.cache.keys().next().value;
            this.cache.delete(firstKey);
        }
        
        this.cache.set(key, {
            value,
            timestamp: Date.now()
        });
    }
    
    get(key) {
        const item = this.cache.get(key);
        if (item) {
            // 更新访问时间(LRU 策略)
            this.cache.delete(key);
            this.cache.set(key, {
                ...item,
                lastAccessed: Date.now()
            });
            return item.value;
        }
        return undefined;
    }
    
    has(key) {
        return this.cache.has(key);
    }
    
    delete(key) {
        return this.cache.delete(key);
    }
    
    clear() {
        this.cache.clear();
    }
    
    get size() {
        return this.cache.size;
    }
    
    // 清理过期缓存
    cleanup(maxAge = 60000) { // 默认 1 分钟
        const now = Date.now();
        for (const [key, item] of this.cache) {
            if (now - item.timestamp > maxAge) {
                this.cache.delete(key);
            }
        }
    }
}

// 使用示例
const cache = new ModernCache(3);

// 可以使用任何类型作为键
const userObj = { id: 1 };
const apiEndpoint = '/api/users';

cache.set(userObj, { name: 'Alice', age: 25 });
cache.set(apiEndpoint, { data: [1, 2, 3] });
cache.set(42, 'number key');
cache.set('string-key', 'string value');

console.log(cache.get(userObj)); // { name: 'Alice', age: 25 }
console.log(cache.size); // 3 (最大容量限制)

// 测试过期清理
setTimeout(() => {
    cache.cleanup(1000); // 清理 1 秒前的缓存
}, 2000);

实战场景2:DOM 元素关联数据

javascript 复制代码
// 传统方法:在 DOM 元素上添加属性
function oldWay() {
    const button = document.createElement('button');
    button.textContent = 'Click me';
    
    // 直接在 DOM 元素上添加自定义属性(不推荐)
    button._customData = {
        clickCount: 0,
        lastClicked: null
    };
    
    button.addEventListener('click', function() {
        this._customData.clickCount++;
        this._customData.lastClicked = new Date();
        console.log('Clicked', this._customData.clickCount, 'times');
    });
    
    return button;
}

// Map 方法:清洁的数据关联
class DOMDataManager {
    constructor() {
        this.elementData = new Map();
    }
    
    setData(element, data) {
        this.elementData.set(element, data);
    }
    
    getData(element) {
        return this.elementData.get(element);
    }
    
    updateData(element, updates) {
        const currentData = this.getData(element) || {};
        this.setData(element, { ...currentData, ...updates });
    }
    
    removeData(element) {
        this.elementData.delete(element);
    }
    
    // 清理已从 DOM 中移除的元素数据
    cleanup() {
        for (const [element] of this.elementData) {
            if (!document.contains(element)) {
                this.elementData.delete(element);
            }
        }
    }
}

// 使用示例
const dataManager = new DOMDataManager();

function createSmartButton(text) {
    const button = document.createElement('button');
    button.textContent = text;
    
    // 使用 Map 存储元素相关数据
    dataManager.setData(button, {
        clickCount: 0,
        lastClicked: null,
        created: new Date()
    });
    
    button.addEventListener('click', function() {
        const data = dataManager.getData(this);
        dataManager.updateData(this, {
            clickCount: data.clickCount + 1,
            lastClicked: new Date()
        });
        
        const updatedData = dataManager.getData(this);
        console.log(`Button clicked ${updatedData.clickCount} times`);
    });
    
    return button;
}

// 创建多个按钮
const button1 = createSmartButton('Button 1');
const button2 = createSmartButton('Button 2');

// 每个按钮都有独立的数据,且不污染 DOM
document.body.appendChild(button1);
document.body.appendChild(button2);

实战场景3:对象关系映射

javascript 复制代码
// 使用 Map 建立对象之间的关系
class RelationshipManager {
    constructor() {
        this.relationships = new Map();
    }
    
    // 建立关系
    relate(object1, object2, relationshipType) {
        if (!this.relationships.has(object1)) {
            this.relationships.set(object1, new Map());
        }
        
        this.relationships.get(object1).set(object2, relationshipType);
    }
    
    // 获取关系
    getRelationship(object1, object2) {
        const relations = this.relationships.get(object1);
        return relations ? relations.get(object2) : undefined;
    }
    
    // 获取所有相关对象
    getRelatedObjects(object, relationshipType = null) {
        const relations = this.relationships.get(object);
        if (!relations) return [];
        
        const result = [];
        for (const [relatedObject, type] of relations) {
            if (!relationshipType || type === relationshipType) {
                result.push({ object: relatedObject, type });
            }
        }
        return result;
    }
    
    // 移除关系
    removeRelationship(object1, object2) {
        const relations = this.relationships.get(object1);
        if (relations) {
            relations.delete(object2);
            if (relations.size === 0) {
                this.relationships.delete(object1);
            }
        }
    }
}

// 实际应用:用户关系系统
const relationManager = new RelationshipManager();

const alice = { name: 'Alice', id: 1 };
const bob = { name: 'Bob', id: 2 };
const charlie = { name: 'Charlie', id: 3 };
const diana = { name: 'Diana', id: 4 };

// 建立关系
relationManager.relate(alice, bob, 'friend');
relationManager.relate(alice, charlie, 'colleague');
relationManager.relate(alice, diana, 'family');
relationManager.relate(bob, charlie, 'friend');

// 查询关系
console.log('Alice 和 Bob 的关系:', 
    relationManager.getRelationship(alice, bob)); // 'friend'

console.log('Alice 的所有朋友:', 
    relationManager.getRelatedObjects(alice, 'friend')); // [{ object: bob, type: 'friend' }]

console.log('Alice 的所有关系:', 
    relationManager.getRelatedObjects(alice));

进阶特性和技巧

1. WeakSet 和 WeakMap

javascript 复制代码
// WeakSet:弱引用的 Set
const weakSet = new WeakSet();
let obj1 = { name: 'Alice' };
let obj2 = { name: 'Bob' };

weakSet.add(obj1);
weakSet.add(obj2);

console.log(weakSet.has(obj1)); // true

// 当对象被垃圾回收时,WeakSet 中的引用也会自动清除
obj1 = null; // 现在 obj1 可能被垃圾回收

// WeakMap:弱引用的 Map
const weakMap = new WeakMap();
let element = document.createElement('div');

// 存储 DOM 元素的私有数据
weakMap.set(element, { 
    clickCount: 0,
    initialized: Date.now()
});

console.log(weakMap.get(element)); // { clickCount: 0, initialized: ... }

// 当 DOM 元素被移除时,相关数据也会被自动清理
element = null;

// 实际应用:私有数据存储
const privateData = new WeakMap();

class SecureUser {
    constructor(name, password) {
        this.name = name;
        // 使用 WeakMap 存储私有数据
        privateData.set(this, {
            password: password,
            loginAttempts: 0,
            lastLogin: null
        });
    }
    
    authenticate(password) {
        const data = privateData.get(this);
        if (data.password === password) {
            data.lastLogin = new Date();
            data.loginAttempts = 0;
            return true;
        } else {
            data.loginAttempts++;
            return false;
        }
    }
    
    getLoginInfo() {
        const data = privateData.get(this);
        return {
            lastLogin: data.lastLogin,
            loginAttempts: data.loginAttempts
        };
    }
}

const user = new SecureUser('Alice', 'secret123');
console.log(user.authenticate('wrong')); // false
console.log(user.authenticate('secret123')); // true
console.log(user.getLoginInfo()); // { lastLogin: ..., loginAttempts: 0 }

// 无法直接访问密码
console.log(user.password); // undefined

2. 迭代和遍历

javascript 复制代码
// Set 的迭代方法
const mySet = new Set(['a', 'b', 'c']);

// 1. for...of 循环
for (const value of mySet) {
    console.log(value); // 'a', 'b', 'c'
}

// 2. forEach 方法
mySet.forEach((value, valueAgain, set) => {
    console.log(value); // 注意:Set 的 forEach 中,value 和 valueAgain 是相同的
});

// 3. 迭代器方法
console.log([...mySet.keys()]);    // ['a', 'b', 'c']
console.log([...mySet.values()]);  // ['a', 'b', 'c']
console.log([...mySet.entries()]); // [['a', 'a'], ['b', 'b'], ['c', 'c']]

// Map 的迭代方法
const myMap = new Map([
    ['name', 'Alice'],
    ['age', 25],
    ['city', 'New York']
]);

// 1. for...of 循环
for (const [key, value] of myMap) {
    console.log(`${key}: ${value}`);
}

// 2. forEach 方法
myMap.forEach((value, key, map) => {
    console.log(`${key} => ${value}`);
});

// 3. 分别迭代键和值
for (const key of myMap.keys()) {
    console.log('Key:', key);
}

for (const value of myMap.values()) {
    console.log('Value:', value);
}

// 4. 迭代条目
for (const [key, value] of myMap.entries()) {
    console.log(`Entry: ${key} = ${value}`);
}

// 实际应用:数据转换
function mapToObject(map) {
    const obj = {};
    for (const [key, value] of map) {
        obj[key] = value;
    }
    return obj;
}

function objectToMap(obj) {
    return new Map(Object.entries(obj));
}

const originalMap = new Map([['a', 1], ['b', 2]]);
const obj = mapToObject(originalMap);
const newMap = objectToMap(obj);

console.log(obj);    // { a: 1, b: 2 }
console.log(newMap); // Map(2) { 'a' => 1, 'b' => 2 }

3. 高级应用:实现 LRU 缓存

javascript 复制代码
// 使用 Map 实现 LRU (Least Recently Used) 缓存
class LRUCache {
    constructor(capacity) {
        this.capacity = capacity;
        this.cache = new Map();
    }
    
    get(key) {
        if (this.cache.has(key)) {
            // 获取值并重新插入到末尾(表示最近使用)
            const value = this.cache.get(key);
            this.cache.delete(key);
            this.cache.set(key, value);
            return value;
        }
        return -1;
    }
    
    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);
    }
    
    // 获取缓存状态
    getState() {
        return Array.from(this.cache.entries());
    }
}

// 测试 LRU 缓存
const lru = new LRUCache(3);

lru.put(1, 'one');
lru.put(2, 'two');
lru.put(3, 'three');
console.log('初始状态:', lru.getState()); // [[1, 'one'], [2, 'two'], [3, 'three']]

lru.get(1); // 访问 1,将其移到最后
console.log('访问 1 后:', lru.getState()); // [[2, 'two'], [3, 'three'], [1, 'one']]

lru.put(4, 'four'); // 添加新元素,删除最久未使用的 2
console.log('添加 4 后:', lru.getState()); // [[3, 'three'], [1, 'one'], [4, 'four']]

4. 性能优化和最佳实践

javascript 复制代码
// 性能比较:Set vs Array 查找
function performanceTest() {
    const size = 100000;
    const array = Array.from({length: size}, (_, i) => i);
    const set = new Set(array);
    
    const searchValues = [0, size/4, size/2, size*3/4, size-1];
    
    console.log('=== 查找性能测试 ===');
    
    // Array.includes() 测试
    console.time('Array includes');
    searchValues.forEach(val => array.includes(val));
    console.timeEnd('Array includes');
    
    // Set.has() 测试
    console.time('Set has');
    searchValues.forEach(val => set.has(val));
    console.timeEnd('Set has');
    
    console.log('=== 添加性能测试 ===');
    
    // Array push 测试
    const testArray = [];
    console.time('Array push');
    for (let i = 0; i < 10000; i++) {
        testArray.push(i);
    }
    console.timeEnd('Array push');
    
    // Set add 测试
    const testSet = new Set();
    console.time('Set add');
    for (let i = 0; i < 10000; i++) {
        testSet.add(i);
    }
    console.timeEnd('Set add');
}

// performanceTest();

// 最佳实践示例
class DataProcessor {
    constructor() {
        this.processedIds = new Set(); // 使用 Set 跟踪已处理的 ID
        this.cache = new Map();        // 使用 Map 作为缓存
        this.config = new Map([        // 使用 Map 存储配置
            ['maxRetries', 3],
            ['timeout', 5000],
            ['batchSize', 100]
        ]);
    }
    
    async processData(items) {
        const results = [];
        const batch = [];
        
        for (const item of items) {
            // 使用 Set 快速检查是否已处理
            if (this.processedIds.has(item.id)) {
                console.log(`Skipping already processed item: ${item.id}`);
                continue;
            }
            
            // 使用 Map 检查缓存
            if (this.cache.has(item.id)) {
                results.push(this.cache.get(item.id));
                continue;
            }
            
            batch.push(item);
            
            // 批量处理
            if (batch.length >= this.config.get('batchSize')) {
                const batchResults = await this.processBatch(batch);
                results.push(...batchResults);
                batch.length = 0; // 清空批次
            }
        }
        
        // 处理剩余项目
        if (batch.length > 0) {
            const batchResults = await this.processBatch(batch);
            results.push(...batchResults);
        }
        
        return results;
    }
    
    async processBatch(items) {
        const results = [];
        
        for (const item of items) {
            try {
                const result = await this.processItem(item);
                
                // 标记为已处理
                this.processedIds.add(item.id);
                
                // 缓存结果
                this.cache.set(item.id, result);
                
                results.push(result);
            } catch (error) {
                console.error(`Error processing item ${item.id}:`, error);
            }
        }
        
        return results;
    }
    
    async processItem(item) {
        // 模拟异步处理
        return new Promise(resolve => {
            setTimeout(() => {
                resolve({
                    id: item.id,
                    processed: true,
                    timestamp: Date.now(),
                    data: item.data?.toUpperCase() || 'NO DATA'
                });
            }, 10);
        });
    }
    
    // 清理方法
    cleanup() {
        this.processedIds.clear();
        this.cache.clear();
    }
    
    // 获取统计信息
    getStats() {
        return {
            processedCount: this.processedIds.size,
            cacheSize: this.cache.size,
            config: Object.fromEntries(this.config)
        };
    }
}

// 使用示例
async function demonstrateProcessor() {
    const processor = new DataProcessor();
    
    const testData = [
        { id: 1, data: 'hello' },
        { id: 2, data: 'world' },
        { id: 1, data: 'hello' }, // 重复 ID
        { id: 3, data: 'test' }
    ];
    
    const results = await processor.processData(testData);
    console.log('处理结果:', results);
    console.log('统计信息:', processor.getStats());
}

// demonstrateProcessor();

何时使用 Set 和 Map?

使用 Set 的场景:

  1. 需要存储唯一值:去重、标签系统、权限管理
  2. 需要高效查找:黑名单检查、已访问页面跟踪
  3. 集合运算:交集、并集、差集操作
  4. 性能敏感的查找操作:大数据量的存在性检查

使用 Map 的场景:

  1. 需要非字符串键:对象作为键、数字键与字符串键区分
  2. 频繁添加删除键值对:动态配置、缓存系统
  3. 需要保持插入顺序:有序的键值对存储
  4. 需要获取大小:经常需要知道键值对数量

避免使用的场景:

  • Set:需要索引访问、需要存储重复值、需要复杂的数据结构
  • Map:只需要简单的字符串键值对、需要 JSON 序列化、需要原型继承

总结

Set 和 Map 是现代 JavaScript 中强大的数据结构,它们不仅解决了传统数组和对象的一些限制,还提供了更好的性能和更清晰的语义。

Set 的核心优势:

  • 自动去重
  • 高效的查找性能(O(1))
  • 清晰的集合语义
  • 丰富的迭代方法

Map 的核心优势:

  • 任意类型的键
  • 保持插入顺序
  • 高效的键值对操作
  • 清晰的大小管理

掌握这两个数据结构,能让你写出更高效、更清晰的 JavaScript 代码。在合适的场景下使用它们,不仅能提升性能,还能让代码的意图更加明确。

记住:选择合适的数据结构是编程的基本功。Set 和 Map 不是为了替代数组和对象,而是为了在特定场景下提供更好的解决方案。


希望这篇文章能帮助你更好地理解和使用 JavaScript 中的 Set 和 Map。如果你有任何问题或想法,欢迎在评论区讨论!

相关推荐
崔庆才丨静觅1 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60612 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了2 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅2 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅3 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅3 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment3 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅4 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊4 小时前
jwt介绍
前端
爱敲代码的小鱼4 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax