Map
Map 对象保存键值对,并且能够记住键的原始插入顺序。任何值(对象或者基本类型)都可以作为一个键或一个值。
使用
new Map([iterable])
特点
- 在映射中用作键和值的对象及其他“集合”类型,在自己的内容或属性被修改时仍然保持不变
- 内存地址不一样视为不同 key
- +0、-0 相同
- NaN 相同
- Map 实例会维护键值对的插入顺序,因此可以根据插入顺序执行迭代操作
静态属性
Map[Symbol.species] 访问器属性会返回一个 Map 构造函数
class MyMap extends Map {
// 重写覆盖 MyMap species to the parent Map constructor
static get [Symbol.species]() {
return Map;
}
}
实例属性
Map.prototype.size
实例方法
-
Map.prototype.set(key, value)- 返回的是当前的 Map 对象
- 链式调用
-
Map.prototype.get(key)如果找不到
key,返回undefined -
Map.prototype.has(key)返回一个布尔值
-
Map.prototype.delete(key)返回 true。如果删除失败,返回 false
-
Map.prototype.clear()没有返回值
-
Map.prototype[@@iterator]()const map1 = new Map(); map1.set('0', 'foo'); map1.set(1, 'bar'); const iterator1 = map1[Symbol.iterator](); for (const item of iterator1) { console.log(item); } // Expected output: Array ["0", "foo"] // Expected output: Array [1, "bar"] -
遍历
Map.prototype.keys()Map.prototype.values()Map.prototype.entries()Map.prototype.forEach(value, key)- 默认迭代器
map[Symbol.iterator] === map.entries
借用数组方法
new Map([...map].filter())new Map([...map].map())
转换
-
Map转为数组[...myMap] -
数组转为
Mapnew Map([]) -
Map转为对象- 都是字符串,它可以无损地转为对象
- 有非字符串的键名,那么这个键名会被转成字符串,再作为对象的键名
- 遍历
Map然后赋值给object
function strMapToObj(strMap) { let obj = Object.create(null); for (let [k, v] of strMap) { obj[k] = v; } return obj; } -
对象转为
MapObject.entries
let obj = { a: 1, b: 2 }; let map = new Map(Object.entries(obj)); function objToStrMap(obj) { let strMap = new Map(); for (let k of Object.keys(obj)) { strMap.set(k, obj[k]); } return strMap; } -
Map转为JSONMap的键名都是字符串,这时可以选择转为对象JSON
function strMapToJson(strMap) { return JSON.stringify(strMapToObj(strMap)); }Map的键名有非字符串,这时可以选择转为JSON数组
function mapToArrayJson(map) { return JSON.stringify([...map]); } -
JSON转为Map- 所有键名都是字符串
function jsonToStrMap(jsonStr) { return objToStrMap(JSON.parse(jsonStr)); }- 整个
JSON就是一个数组,且每个数组成员本身,又是一个有两个成员的数组
function jsonToMap(jsonStr) { return new Map(JSON.parse(jsonStr)); }
使用场景
- 内存占用 Map
- 插入性能 Map
- 查找速度 Object
- 删除性能 Map
Map 和 Object
- 如果键在运行时才能知道,或者所有的键类型相同,所有的值类型相同,那就使用
Map - 如果需要将原始值存储为键,则使用
Map - 如果需要对个别元素进行操作,使用
Object
意外的键
- Map 默认情况不包含任何键。只包含显式插入的键
- 一个 Object 有一个原型,原型链上的键名有可能和你自己在对象上的设置的键名产生冲突
键的类型
- 一个 Map 的键可以是任意值,包括函数、对象或任意基本类型
- 一个 Object 的键必须是一个 String 或是 Symbol
键的顺序
- Map 中的键是有序的。因此,当迭代的时候,一个 Map 对象以插入的顺序返回键值
- 虽然 Object 的键目前是有序的,但并不总是这样,而且这个顺序是复杂的。因此,最好不要依赖属性的顺序
Size
- Map 的键值对个数可以轻易地通过 size 属性获取
- Object 的键值对个数只能手动计算
迭代
- Map 是可迭代的的,所以可以直接被迭代
- Object 没有实现迭代协议,所以使用 JavaSctipt 的 for...of 表达式并不能直接迭代对象
性能
- 在频繁增删键值对的场景下表现更好
- 在频繁添加和删除键值对的场景下未作出优化
序列化和解析
- Map 没有元素的序列化和解析的支持
- 原生的由 Object 到 JSON 的序列化支持,使用 JSON.stringify(),原生的由 JSON 到 Object 的解析支持,使用 JSON.parse()
WeakMap
WeakMap 对象是一组键/值对的集合,其中的键是弱引用的。其键必须是对象,而值可以是任意的。
实例方法
WeakMap.prototype.delete(key)WeakMap.prototype.get(key)WeakMap.prototype.has(key)WeakMap.prototype.set(key, value)
特点
- 只接受对象作为键名(null除外),不接受其他类型的值作为键名
- 键名所指向的对象,不计入垃圾回收机制
- 不能遍历操作
用途
DOM 节点作为键名
const m = new WeakMap();
const loginButton = document.querySelector("#login");
// Associates some metadata with the node
m.set(loginButton, { disabled: false });
loginButton.addEventListener(
"click",
function () {
m.set(loginButton, { disabled: true });
},
false
);
部署私有属性
const User = (() => {
const wm = new WeakMap();
class User {
constructor(id) {
this.idProperty = Symbol("id");
this.setId(id);
}
setPrivate(property, value) {
const privateMembers = wm.get(this) || {};
privateMembers[property] = value;
wm.set(this, privateMembers);
}
getPrivate(property) {
return wm.get(this)[property];
}
setId(id) {
this.setPrivate(this.idProperty, id);
}
getId(id) {
return this.getPrivate(this.idProperty);
}
}
return User;
})();
const user = new User(123);
alert(user.getId()); // 123
user.setId(456);
alert(user.getId()); // 456