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。如果你有任何问题或想法,欢迎在评论区讨论!

相关推荐
用户03048059126310 小时前
null 与 undefined 的区别
前端
朱程10 小时前
写给自己的 LangChain 开发教程(四):RAG(1)
前端·人工智能
alphardex10 小时前
现代 Web 的视觉组件探索
前端·html·web components
子兮曰10 小时前
🎯 UnoCSS终极速查表:这些原子类让你开发效率翻倍!
前端·css·前端工程化
{⌐■_■}10 小时前
【计算机网络】前端基础知识Cookie、localStorage、sessionStorage 以及 Token
前端·计算机网络
CF14年老兵10 小时前
努力生活,本身就是一种成就
前端·后端·trae
w_y_fan10 小时前
Route.settings.name 的设置与产生机制
前端·flutter
南雨北斗10 小时前
Vue3中watch的应用场景
前端
flyliu10 小时前
常见的攻击方式有哪些,如何防御
前端·安全