目录
- 一、Object
- 二、Map
- 三、WeakMap
- 四、区别比较
-
- [4.1 基础操作比较](#4.1 基础操作比较)
- [4.2 性能对比示例](#4.2 性能对比示例)
- [4.3 完整区别对比代码](#4.3 完整区别对比代码)
- 五、总结
-
- [5.1 选择建议](#5.1 选择建议)
- [5.2 核心区别对比](#5.2 核心区别对比)
一、Object
作用:
- 最基础的键值对存储结构,键只能是字符串或 Symbol。
特点:
-
键必须是字符串或 Symbol
-
有原型链,包含默认属性
-
键值对数量需要手动计算
-
性能稳定,适合存储结构化数据
业务场景:
javascript
// 场景1:实体对象/DTO
class User {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
// 场景2:配置对象
const config = {
apiUrl: 'https://api.example.com',
timeout: 5000,
retryCount: 3
};
// 场景3:方法集合
const utils = {
formatDate(date) {
return date.toLocaleDateString();
},
parseJSON(str) {
try {
return JSON.parse(str);
} catch {
return null;
}
}
};
二、Map
作用:
- 完善的键值对集合,键可以是任意类型。
特点:
-
键可以是任意类型(对象、函数、原始值)
-
保持插入顺序
-
有 size 属性,方便获取大小
-
性能优于 Object(频繁增删场景)
业务场景:
javascript
// 场景1:缓存系统
const cache = new Map();
function fetchData(key) {
if (cache.has(key)) {
console.log('从缓存获取');
return cache.get(key);
}
const data = `数据-${key}`; // 模拟获取数据
cache.set(key, data);
// 设置过期时间(5秒)
setTimeout(() => {
cache.delete(key);
console.log(`缓存 ${key} 已过期`);
}, 5000);
return data;
}
// 场景2:DOM节点关联数据
const elementData = new Map();
const button = document.querySelector('#btn');
elementData.set(button, {
clickCount: 0,
lastClickTime: null
});
button.addEventListener('click', () => {
const data = elementData.get(button);
data.clickCount++;
data.lastClickTime = new Date();
});
// 场景3:对象键的场景
const objKey1 = { id: 1 };
const objKey2 = { id: 2 };
const map = new Map();
map.set(objKey1, '值1');
map.set(objKey2, '值2');
console.log(map.get(objKey1)); // '值1'
三、WeakMap
作用:
- 弱引用的 Map,键必须是对象,不会阻止垃圾回收。
特点:
-
键只能是对象
-
弱引用,不阻止垃圾回收
-
不可迭代,没有 size 属性
-
自动处理内存管理
业务场景:
javascript
// 场景1:私有数据存储
const privateData = new WeakMap();
class Person {
constructor(name, age) {
privateData.set(this, {
name,
age,
_secret: '私有信息'
});
}
getName() {
return privateData.get(this).name;
}
getSecret() {
return privateData.get(this)._secret;
}
}
const person = new Person('张三', 25);
console.log(person.getName()); // '张三'
// 无法直接访问私有数据
console.log(person._secret); // undefined
// 场景2:DOM节点元数据(无需手动清理)
const domMetadata = new WeakMap();
function trackElement(element, metadata) {
domMetadata.set(element, metadata);
}
// 当元素被移除时,关联的metadata会自动被垃圾回收
const div = document.createElement('div');
trackElement(div, { created: Date.now(), visits: 0 });
// 场景3:缓存计算结果(避免内存泄漏)
const computedCache = new WeakMap();
function processObject(obj) {
if (!computedCache.has(obj)) {
const result = { /* 复杂计算 */ };
computedCache.set(obj, result);
}
return computedCache.get(obj);
}
// 当obj不再被使用时,缓存会自动释放
四、区别比较
4.1 基础操作比较
javascript
// 1. 键类型的区别
const obj = {};
obj[{}] = '对象键'; // 实际上键会被转为 '[object Object]'
obj[function(){}] = '函数键'; // 转为字符串
const map = new Map();
map.set({}, '对象键'); // 正常
map.set(() => {}, '函数键'); // 正常
const weakMap = new WeakMap();
// weakMap.set('string', '值'); // 错误!键必须是对象
weakMap.set({}, '对象键'); // 正常
// 2. 迭代和大小获取
const userObj = { name: '张三', age: 25 };
console.log(Object.keys(userObj).length); // 2
console.log(Object.entries(userObj)); // [['name','张三'],['age',25]]
const userMap = new Map([['name','张三'], ['age',25]]);
console.log(userMap.size); // 2
userMap.forEach((value, key) => console.log(key, value));
// WeakMap 无法迭代
const userWeakMap = new WeakMap();
const key = { id: 1 };
userWeakMap.set(key, { name: '张三' });
// console.log(userWeakMap.size); // undefined
// userWeakMap.forEach // 不存在这个方法
4.2 性能对比示例
javascript
// 性能测试:频繁增删操作
console.time('Object');
const obj = {};
for (let i = 0; i < 100000; i++) {
obj[`key${i}`] = i;
}
for (let i = 0; i < 100000; i++) {
delete obj[`key${i}`];
}
console.timeEnd('Object');
console.time('Map');
const map = new Map();
for (let i = 0; i < 100000; i++) {
map.set(`key${i}`, i);
}
for (let i = 0; i < 100000; i++) {
map.delete(`key${i}`);
}
console.timeEnd('Map');
// 内存测试:WeakMap 自动回收
let obj1 = { data: new Array(1000000).fill('*') };
let obj2 = { data: new Array(1000000).fill('*') };
const map2 = new Map();
map2.set(obj1, '元数据1');
const weakMap2 = new WeakMap();
weakMap2.set(obj2, '元数据2');
obj1 = null; // map2 仍然持有引用,内存不会释放
obj2 = null; // weakMap2 的引用会被垃圾回收
// 稍后检查内存使用情况
setTimeout(() => {
console.log('Map size:', map2.size); // 1
// WeakMap 无法检查 size
}, 5000);
4.3 完整区别对比代码
javascript
// 创建一个详细的对比示例
function compareStructures() {
// 1. 键类型对比
console.group('键类型对比');
const obj = {};
const map = new Map();
const weakMap = new WeakMap();
const keyObj = { id: 1 };
const keyFunc = function() {};
// Object
obj[keyObj] = 'object value';
obj[keyFunc] = 'function value';
console.log('Object 字符串化键:', obj['[object Object]']); // 'object value'
// Map
map.set(keyObj, 'map object value');
map.set(keyFunc, 'map function value');
console.log('Map 对象键:', map.get(keyObj)); // 'map object value'
// WeakMap
weakMap.set(keyObj, 'weakmap value');
console.log('WeakMap 对象键:', weakMap.get(keyObj)); // 'weakmap value'
console.groupEnd();
// 2. 顺序保证
console.group('顺序保证');
const orderObj = {};
orderObj.b = 2;
orderObj.a = 1;
orderObj.c = 3;
console.log('Object 键顺序:', Object.keys(orderObj)); // ['b', 'a', 'c'] 或类似
const orderMap = new Map();
orderMap.set('b', 2);
orderMap.set('a', 1);
orderMap.set('c', 3);
console.log('Map 键顺序:', Array.from(orderMap.keys())); // ['b', 'a', 'c']
console.groupEnd();
// 3. 大小获取
console.group('大小获取');
const sizeObj = { a: 1, b: 2, c: 3 };
console.log('Object 大小:', Object.keys(sizeObj).length); // 3
const sizeMap = new Map([['a',1], ['b',2], ['c',3]]);
console.log('Map 大小:', sizeMap.size); // 3
console.groupEnd();
// 4. 迭代方式
console.group('迭代方式');
const iterObj = { name: '张三', age: 25 };
console.log('Object 迭代:');
for (const [key, value] of Object.entries(iterObj)) {
console.log(key, value);
}
const iterMap = new Map([['name', '张三'], ['age', 25]]);
console.log('Map 迭代:');
for (const [key, value] of iterMap) {
console.log(key, value);
}
console.groupEnd();
}
// 5. 内存泄漏演示
function memoryLeakDemo() {
console.group('内存管理对比');
let mapLeak = new Map();
let weakMapNoLeak = new WeakMap();
(function() {
const obj1 = { name: '会泄漏的对象' };
const obj2 = { name: '不会泄漏的对象' };
mapLeak.set(obj1, '数据');
weakMapNoLeak.set(obj2, '数据');
})();
// 此时 obj1 仍被 mapLeak 引用,不会被回收
// obj2 只被 weakMapNoLeak 弱引用,可以被回收
console.log('Map 仍有引用,可能造成内存泄漏');
console.log('WeakMap 不会阻止垃圾回收');
console.groupEnd();
}
// 运行对比
compareStructures();
memoryLeakDemo();
五、总结
5.1 选择建议
使用 Object :
-
需要存储简单的键值对,键是字符串
-
需要创建对象实例(类实例)
-
需要 JSON 序列化
-
需要原型继承
-
结构化数据(如配置、DTO)
使用 Map :
-
键不是字符串(对象、函数等)
-
需要频繁增删键值对
-
需要保持插入顺序
-
需要便捷的大小属性(size)
-
需要频繁迭代
使用 WeakMap :
-
键必须是对象
-
需要自动内存管理,防止内存泄漏
-
存储 DOM 节点的元数据
-
实现私有属性
-
不需要迭代操作
5.2 核心区别对比

