Map
1. 完整的例子
let map = new Map();
map.set("foo", 1);
map.set("bar", 2);
//或者一步到位直接初始化
let map = new Map([
["foo", 1],
["bar", 2],
]);
let value1 = map.get("foo");
console.log(value1); // 1
let value2 = map.has("bar");
console.log(value2); // true
map.delete("bar");
let value3 = map.get("bar");
console.log(value3); // undefined
map.clear();
let value4 = map.size;
console.log(value4); // 0
其实 javescript 对于 Object 的原生支持,就已经起到了 Map 的作用
比如上面的可以这样表达
let map = {};
map["foo"] = 1;
map["bar"] = 2;
//或者一步到位直接初始化
let map = {
foo: 1,
bar: 2,
};
let value1 = map["foo"];
console.log(value1); // 1
let value2 = map["bar"] !== undefined;
console.log(value2); // true
delete map["bar"];
let value3 = map["bar"];
console.log(value3); // undefined
map = {};
let value4 = Object.keys(map).length;
console.log(value4); // 0
-
用 Object 模拟时,要注意
-
判断对象是否存在某个属性键时,更准确地要用 ES2022 中的
hasOwnlet map = { foo: 1, bar: 2, }; let value2 = Object.hasOwn(map, "bar"); console.log(value2); // true -
判断对象的长度时,用 keys 返回的是可枚举的属性,如果要包括
enumerable: false的话,需要用getOwnPropertyNameslet map = { foo: 1, bar: 2, }; let value4 = Object.getOwnPropertyNames(map).length; console.log(value4); // 2-
以下是二者区别的测试代码
const obj = {}; Object.defineProperties(obj, { property1: { enumerable: true, value: 1 }, property2: { enumerable: false, value: 2 }, }); console.log(Object.keys(obj)); // ["property1"] console.log(Object.getOwnPropertyNames(obj)); // ["property1", "property2"]
-
-
2. 要点
-
Map 可以使用任何类型作为键,而对象只能使用字符串或 Symbol
let map = new Map(); let objKey = { id: 1 }; let funcKey = function () {}; map.set(objKey, "对象作为键"); map.set(funcKey, "函数作为键"); map.set(123, "数字作为键"); console.log(map.get(objKey)); // "对象作为键" console.log(map.get(funcKey)); // "函数作为键" console.log(map.get(123)); // "数字作为键" // 对象只能用字符串或 Symbol let obj = {}; obj[objKey] = "值"; // objKey 会被转换为字符串 "[object Object]" console.log(obj["[object Object]"]); // "值" -
Map 保持插入顺序,迭代时按照插入顺序进行
let map = new Map(); map.set("z", 1); map.set("a", 2); map.set("m", 3); for (let [key, value] of map) { console.log(key); // 输出顺序:z, a, m(按插入顺序) } -
Map 提供了丰富的迭代方法
let map = new Map([ ["foo", 1], ["bar", 2], ]); // keys() - 获取所有键 for (let key of map.keys()) { console.log(key); // "foo", "bar" } // values() - 获取所有值 for (let value of map.values()) { console.log(value); // 1, 2 } // entries() - 获取所有键值对 for (let [key, value] of map.entries()) { console.log(key, value); // "foo" 1, "bar" 2 } // forEach map.forEach((value, key) => { console.log(key, value); }); -
Map 没有原型链污染问题,更加安全
// 对象可能会有原型链上的属性 let obj = {}; console.log(obj["toString"]); // function toString() { [native code] } console.log("toString" in obj); // true(来自原型链) // Map 不会有这个问题 let map = new Map(); console.log(map.get("toString")); // undefined console.log(map.has("toString")); // false -
在频繁增删键值对的场景下,Map 的性能优于对象
// Map 在大量增删操作时性能更好 let map = new Map(); for (let i = 0; i < 1000000; i++) { map.set(`key${i}`, i); } for (let i = 0; i < 1000000; i++) { map.delete(`key${i}`); } -
Map 与对象的转换
// 对象转 Map let obj = { foo: 1, bar: 2 }; let map = new Map(Object.entries(obj)); // Map 转对象 let map = new Map([ ["foo", 1], ["bar", 2], ]); let obj = Object.fromEntries(map); console.log(obj); // { foo: 1, bar: 2 } -
WeakMap:弱引用版本的 Map
// WeakMap 的键必须是对象,且是弱引用 let wm = new WeakMap(); let key = { id: 1 }; wm.set(key, "值"); console.log(wm.get(key)); // "值" // 当 key 没有其他引用时,会被垃圾回收 key = null; // WeakMap 中的条目也会被自动清理 // WeakMap 没有 size、clear、keys、values、entries 等方法 // 只有 get、set、has、delete-
WeakMap 的典型应用场景:存储对象的私有数据、缓存
// 用 WeakMap 存储 DOM 元素的关联数据 let elementData = new WeakMap(); let element = document.querySelector("#myElement"); elementData.set(element, { clicks: 0 }); // 当 element 从 DOM 移除且没有其他引用时 // WeakMap 中的数据会被自动清理,避免内存泄漏
-