ES6 Map 数据结构是用总结
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); // 2
let 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
- 注意内存管理,及时清理不需要的数据