一、設計思想與核心概念
1. 解決傳統結構的痛點
- Object:鍵只能是字符串/Symbol、無序、無size屬性
- Array:查找效率低(
O(n)
)、無自動去重機制 - 核心突破:
// 傳統方式 vs ES6方式 const obj = { [{}]: 'value' }; // 鍵會被轉為"[object Object]" const map = new Map().set({}, '真實對象鍵'); // 保留原始鍵類型
2. 四大金剛特性對比
結構 | 鍵類型 | 值特性 | 可迭代 | 弱引用 | 典型內存消耗(1w條) |
---|---|---|---|---|---|
Map | 任意類型 | 任意數據 | ?? | ? | ~2.4MB |
WeakMap | 僅對象 | 任意數據 | ? | ?? | ~0.7MB |
Set | - | 唯一值 | ?? | ? | ~1.8MB |
WeakSet | 僅對象 | 唯一對象 | ? | ?? | ~0.5MB |
二、實例方法與屬性詳解
1. Map 方法全表
方法/屬性 | 語法示例 | 時間復雜度 | 說明 |
---|---|---|---|
size | map.size | O(1) | 實時條目計數 |
set(key, value) | map.set({id:1}, 'data') | O(1) | 返回Map本身(可鏈式調用) |
get(key) | map.get('name') | O(1) | 未找到返回undefined |
has(key) | map.has(NaN) | O(1) | 返回布爾值 |
delete(key) | map.delete(key) | O(1) | 返回操作是否成功 |
clear() | map.clear() | O(n) | 清空所有條目 |
keys() | for(const k of map.keys()) | O(n) | 返回鍵的迭代器 |
values() | map.values() | O(n) | 返回值的迭代器 |
entries() | map.entries() | O(n) | 返回[key, value]迭代器(默認) |
forEach(callback) | map.forEach((v,k)=>...) | O(n) | 遍歷方法 |
2. WeakMap 特殊方法
const wm = new WeakMap();
const obj = {};// 僅三個方法可用
wm.set(obj, 'private'); // ??
wm.get(obj); // ??
wm.has(obj); // ??
wm.delete(obj); // ??// 以下操作均不支持
wm.size; // ? undefined
wm.clear(); // ? 方法不存在
wm.keys(); // ? 不可迭代
3. Set 核心方法
方法/屬性 | 示例 | 說明 |
---|---|---|
add(value) | set.add(100).add(200) | 鏈式調用,自動去重 |
delete(value) | set.delete(NaN) | 返回操作結果 |
has(value) | set.has('test') | 存在性檢測 |
entries() | set.entries() | 返回[value, value]迭代器 |
4. WeakSet 方法限制
const ws = new WeakSet();
const obj = {};ws.add(obj); // ?? 僅對象
ws.has(obj); // ??
ws.delete(obj); // ??ws.add('str'); // ? TypeError
ws.size; // ? undefined
三、使用場景與技巧
1. Map 最佳實踐
場景1:DOM節點元數據存儲
const nodeMap = new Map();function handleClick(node) {if (!nodeMap.has(node)) {nodeMap.set(node, {clickCount: 0,lastClick: null});}const meta = nodeMap.get(node);meta.clickCount++;meta.lastClick = Date.now();
}
場景2:配置優先級隊列
class PriorityQueue {constructor() {this.map = new Map([[1, []], [2, []], [3, []]]);}add(item, priority) {this.map.get(priority).push(item);}process() {for (const [level, tasks] of this.map) {while(tasks.length) {this.execute(tasks.shift());}}}
}
2. WeakMap 高級用法
實現真正私有屬性
const privateStore = new WeakMap();class BankAccount {constructor(balance) {privateStore.set(this, {balance: balance,transactions: []});}deposit(amount) {const data = privateStore.get(this);data.balance += amount;data.transactions.push({ type: 'deposit', amount });}get balance() {return privateStore.get(this).balance;}
}
深度克隆輔助
function deepClone(obj, map = new WeakMap()) {if (obj === null || typeof obj !== 'object') return obj;if (map.has(obj)) return map.get(obj);const clone = Array.isArray(obj) ? [] : {};map.set(obj, clone);for (const key in obj) {if (obj.hasOwnProperty(key)) {clone[key] = deepClone(obj[key], map);}}return clone;
}
3. Set 實戰技巧
場景:用戶權限校驗
const ADMIN = Symbol('admin');
const EDITOR = Symbol('editor');const userPermissions = new Set([ADMIN, EDITOR]);function canDeleteContent(user) {return userPermissions.has(ADMIN);
}// 動態權限管理
function updatePermissions(user, perm) {userPermissions.add(perm);
}
高效數組去重
// 傳統方式 vs Set方式
const arr = [1,2,2,3,3,3];// O(n^2)
const uniqueArr = arr.filter((v,i) => arr.indexOf(v) === i);// O(n)
const setWay = [...new Set(arr)];
4. WeakSet 特殊應用
防止循環引用
const seen = new WeakSet();function safeStringify(obj) {if (typeof obj === 'object' && obj !== null) {if (seen.has(obj)) throw new Error('循環引用');seen.add(obj);}// ...序列化處理
}
四、性能優化策略
1. 基準測試對比(V8引擎)
操作 | Map(1e6次) | Object(1e6次) | 差異 |
---|---|---|---|
插入操作 | ~120ms | ~95ms | -17% |
讀取操作 | ~65ms | ~80ms | +23% |
刪除操作 | ~110ms | ~450ms | +309% |
迭代操作 | ~200ms | ~350ms | +75% |
優化建議:
- 大數據集(>10萬條):優先使用Map
- 頻繁刪除:必須使用Map
- 內存敏感:考慮WeakMap/WeakSet
2. 內存管理技巧
// 錯誤示范:內存泄漏
const cache = new Map();
function processData(data) {cache.set(data.id, data); // 長期持有引用,data無法被GC
}// 正確做法:WeakMap自動清理
const weakCache = new WeakMap();
function processObj(obj) {weakCache.set(obj, Date.now());
}
五、對比總結與決策矩陣
1. 四維對比表
特性 | Map | WeakMap | Set | WeakSet |
---|---|---|---|---|
鍵類型 | Any | Object | - | Object |
值唯一性 | No | No | Yes | Yes |
可序列化 | Yes | No | Yes | No |
垃圾回收 | 強引用 | 弱引用 | 強引用 | 弱引用 |
迭代能力 | Full | None | Full | None |
典型內存占用 | 較高 | 較低 | 中等 | 最低 |
2. 場景決策樹
需要鍵值對存儲?
├─ 需要非對象鍵 → Map
├─ 需要內存優化 → WeakMap
└─ 需要簡單標記 → Set/WeakSet需要保證元素唯一?
├─ 基本類型 → Set
└─ 對象類型 → WeakSet數據生命周期?
├─ 長期存在 → Map/Set
└─ 臨時使用 → WeakMap/WeakSet
3. 綜合建議
- 優先考慮Map:當需要鍵值對且不確定數據結構時
- 內存敏感場景:使用Weak系列,如DOM節點關聯數據
- 高性能去重:無腦選擇Set替代數組方案
- 私有數據管理:WeakMap是最佳選擇