1. Map 基本概念
Map 是 ES6 提供的新的數據結構,它類似于對象,但是"鍵"的范圍不限于字符串,各種類型的值(包括對象)都可以當作鍵。Map 也可以跟蹤鍵值對的原始插入順序。
1.1 基本用法
// 創建一個空Map
const map = new Map();// 創建一個帶有初始值的Map
const map1 = new Map([['name', 'John'],['age', 25]
]);// 使用對象作為鍵
const objKey = { id: 1 };
const map2 = new Map();
map2.set(objKey, 'value for object');// Map的大小
console.log(map1.size); // 2let myMap = new Map();
myMap.set("name", "Alice");
myMap.set(1, "one");
myMap.set(true, "boolean");
console.log(myMap); // Map { 'name' => 'Alice', 1 => 'one', true => 'boolean' }
1.2 Map的基本方法
const map = new Map();// 添加鍵值對
map.set('name', 'John');
map.set('age', 25).set('city', 'New York'); // 支持鏈式調用// 獲取值
console.log(map.get('name')); // 'John'// 檢查鍵是否存在
console.log(map.has('age')); // true// 刪除鍵值對
map.delete('age');// 清空Map
map.clear();// 獲取Map的大小
console.log(map.size); // 0
方法 | 說明 |
---|---|
set(key, value) | 添加鍵值對 |
get(key) | 獲取鍵對應的值 |
delete(key) | 刪除某個鍵值對 |
has(key) | 判斷某個鍵是否存在 |
clear() | 清空 Map |
size | 獲取鍵值對數量 |
console.log(myMap.get("name")); // Alice
console.log(myMap.has(1)); // true
myMap.delete(true);
console.log(myMap.size); // 2
myMap.clear();
console.log(myMap.size); // 0
2. Map的遍歷
2.1 遍歷方法
const map = new Map([['name', 'John'],['age', 25],['city', 'New York']
]);// keys() - 返回鍵名的遍歷器
for (let key of map.keys()) {console.log(key);
}// values() - 返回鍵值的遍歷器
for (let value of map.values()) {console.log(value);
}// entries() - 返回鍵值對的遍歷器
for (let [key, value] of map.entries()) {console.log(key, value);
}// forEach() - 使用回調函數遍歷
map.forEach((value, key) => {console.log(key, value);
});let map = new Map([["name", "Alice"],["age", 25],["gender", "female"]
]);console.log([...map.keys()]); // ['name', 'age', 'gender']
console.log([...map.values()]); // ['Alice', 25, 'female']
console.log([...map.entries()]); // [['name', 'Alice'], ['age', 25], ['gender', 'female']]
2.2 與對象的轉換
// Map轉對象
function mapToObject(map) {const obj = {};for (let [key, value] of map) {obj[key] = value;}return obj;
}// 對象轉Map
function objectToMap(obj) {return new Map(Object.entries(obj));
}// 使用示例
const map = new Map([['name', 'John'], ['age', 25]]);
const obj = mapToObject(map);
const backToMap = objectToMap(obj);
3. 實際應用場景
3.1 緩存系統
class Cache {constructor() {this.cache = new Map();}set(key, value, ttl = 60000) { // 默認緩存1分鐘const expires = Date.now() + ttl;this.cache.set(key, { value, expires });}get(key) {const data = this.cache.get(key);if (!data) return null;if (Date.now() > data.expires) {this.cache.delete(key);return null;}return data.value;}clear() {this.cache.clear();}
}// 使用示例
const cache = new Cache();
cache.set('user', { id: 1, name: 'John' }, 5000); // 緩存5秒
console.log(cache.get('user')); // { id: 1, name: 'John' }
3.2 狀態管理
class StateManager {constructor() {this.states = new Map();this.listeners = new Map();}setState(key, value) {this.states.set(key, value);if (this.listeners.has(key)) {this.listeners.get(key).forEach(listener => listener(value));}}getState(key) {return this.states.get(key);}subscribe(key, callback) {if (!this.listeners.has(key)) {this.listeners.set(key, new Set());}this.listeners.get(key).add(callback);}
}// 使用示例
const store = new StateManager();
store.subscribe('theme', theme => console.log(`Theme changed to ${theme}`));
store.setState('theme', 'dark'); // 輸出: Theme changed to dark
3.3 事件總線
class EventBus {constructor() {this.events = new Map();}on(event, callback) {if (!this.events.has(event)) {this.events.set(event, new Set());}this.events.get(event).add(callback);}off(event, callback) {if (this.events.has(event)) {this.events.get(event).delete(callback);}}emit(event, data) {if (this.events.has(event)) {this.events.get(event).forEach(callback => callback(data));}}
}// 使用示例
const bus = new EventBus();
bus.on('userLogin', user => console.log(`${user.name} logged in`));
bus.emit('userLogin', { name: 'John' }); // 輸出: John logged in
3.4 數據結構映射
class BiMap {constructor() {this.forward = new Map();this.reverse = new Map();}set(key, value) {this.forward.set(key, value);this.reverse.set(value, key);}getByKey(key) {return this.forward.get(key);}getByValue(value) {return this.reverse.get(value);}
}// 使用示例
const userRoles = new BiMap();
userRoles.set('john', 'admin');
console.log(userRoles.getByKey('john')); // 'admin'
console.log(userRoles.getByValue('admin')); // 'john'
4. WeakMap
WeakMap 是一種特殊的 Map,它只接受對象作為鍵,并且鍵是弱引用。
4.1 基本用法
const wm = new WeakMap();
let key = { id: 1 };
wm.set(key, 'value');console.log(wm.get(key)); // 'value'
key = null; // key對象可被垃圾回收
4.2 實際應用場景
// 使用 WeakMap 存儲私有數據
const privateData = new WeakMap();class User {constructor(name, age) {privateData.set(this, { name, age });}getName() {return privateData.get(this).name;}getAge() {return privateData.get(this).age;}
}const user = new User('John', 25);
console.log(user.getName()); // 'John'
4.3 Map 與 WeakMap 的區別**
特性 | Map | WeakMap |
---|---|---|
是否存儲值 | ? 是 | ? 是 |
是否存儲對象 | ? 是 | ? 僅能存對象 |
是否支持遍歷 | ? 是 | ? 不能遍歷 |
是否允許自動垃圾回收 | ? 否 | ? 是 |
📌 WeakMap
適用于 存儲對象的私有數據,對象若被其他變量引用解除,會被自動回收,不會造成內存泄漏。
let weakMap = new WeakMap();
let obj = { name: "John" };
weakMap.set(obj, "privateData");
console.log(weakMap.get(obj)); // privateData
obj = null; // 當對象無引用時會被垃圾回收
5. 性能考慮
5.1 Map vs Object
特性 | Map | Object |
---|---|---|
鍵的類型 | 任意類型 | 僅字符串或 Symbol |
是否有默認原型 | ? 否 | ? 是(Object.prototype ) |
遍歷順序 | 按插入順序 | 無序(ES6 之后部分實現有序) |
適合場景 | 需要鍵值存儲,鍵可為任意類型 | 傳統鍵值對、JSON 結構 |
// Map 在頻繁增刪鍵值對時性能更好
const map = new Map();
const obj = {};console.time('Map');
for (let i = 0; i < 1000000; i++) {map.set(i, i);map.delete(i);
}
console.timeEnd('Map');console.time('Object');
for (let i = 0; i < 1000000; i++) {obj[i] = i;delete obj[i];
}
console.timeEnd('Object');let obj = {};
obj["1"] = "one";
obj[1] = "number one";
console.log(obj); // { '1': 'number one' } 因為 Object 的鍵被轉換為字符串let map = new Map();
map.set("1", "one");
map.set(1, "number one");
console.log(map); // Map { '1' => 'one', 1 => 'number one' }
5.2 內存優化
// 及時清理不需要的數據
function processData(data) {const map = new Map();// 處理數據...map.clear(); // 及時清空釋放內存
}
6. 最佳實踐
- 需要非字符串鍵時使用 Map
- 需要保持鍵值對順序時使用 Map
- 頻繁增刪鍵值對時使用 Map
- 存儲私有數據時考慮使用 WeakMap
- 需要遍歷鍵值對時使用 Map
- 注意內存管理,及時清理不需要的數據