Map
JavaScript 的对象(
Object
),本质上是键值对的集合(Hash 结构
),但是传统上只能用字符串当作键。这给它的使用带来了很大的限制。
为了解决这个问题,ES6 提供了
Map
数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键,是一种更完善的 Hash 结构实现。
生成Map实例
const map1 = new Map()
const map2 = new Map([
['name', '张三'],
['title', 'Author']
])
Map实例的属性
- Map.prototype.size:返回Map实例的成员总数。
四个操作方法
Map.prototype.set(key,value)
:设置键名key
对应的键值为value
,然后返回整个 Map 结构。如果key
已经有值,则键值会被更新,否则就新生成该键。Map.prototype.get(key)
:读取key
对应的键值,如果找不到key
,返回undefined
。Map.prototype.has(key)
:返回一个布尔值,表示某个键是否在当前Map
对象之中。Map.prototype.delete(key)
:删除某个键,返回true
。如果删除失败,返回false
。Map.prototype.clear()
:清除所有成员,没有返回值。
四个遍历方法
Map.prototype.keys()
:返回键名遍历器Map.prototype.values()
:返回键值遍历器Map.prototype.entries()
:返回键值对遍历器Map.prototype.forEach()
:使用回调函数遍历每个成员
应用
- 扩展对象
- 当我们有一系列对象,想记录每个对象一种属性,这时候就可以使用
Map
扩展对象
- 当我们有一系列对象,想记录每个对象一种属性,这时候就可以使用
let notebook = new Map();
chickenList.forEach(function(chicken, index){
notebook.set(chicken, getWeight(chicken));
});
避免修改了原有对象,可能导致意外的行为
避免对象冻结了或者有不可覆盖的属性
避免与对象原有属性冲突
避免无法精准地对比到是哪一个对象
- 完善私有属性的实现
之前的Symbol实现的私有属性的版本里,仍然存在着可以被特殊api遍历的缺陷。
基于Map的解决思路:用一个闭包内的Map来扩展每个生成的对象
let Person = (function() {
let map = new Map()
function Person(name,age) {
let prvateProperty = {
name:name,
age:age
}
map.set(this,prvateProperty )
}
Person.prototype.getName = function() {
return map.get(this).name
}
Person.prototype.age= function() {
return map.get(this).age
}
return Person;
}());
WeakMap
与WeakSet
类似,WeakMap
与 Map
有两个区别。
WeakMap
的键只能是对象,而不能是其他类型的值。WeakMap
中对键的引用是弱引用
同样地,WeakMap
不能遍历,是因为成员都是弱引用,随时可能消失。
WeakMap
只有四个方法可用:get()
、set()
、has()
、delete()
。
注意:WeakMap
弱引用的只是键名,而不是键值。键值依然是正常引用。
const wm = new WeakMap();
let key = {};
let obj = {foo: 1};
wm.set(key, obj);
obj = null;
wm.get(key)
- 完善私有属性的实现
前面基于Map的实现还存在一个问题:
当Person实例的外部引用消除时,闭包中的Map仍然有Person实例作为键的引用,Person实例不会被垃圾回收,必须等到所有的Person实例的外部引用消除,Map所在的闭包也会消除,最后Person实例才会被垃圾回收
let Person = (function() {
let wm = new WeakMap()
function Person(name,age) {
let prvateProperty = {
name:name,
age:age
}
wm.set(this,prvateProperty )
}
Person.prototype.getName = function() {
return wm.get(this).name
}
Person.prototype.age= function() {
return wm.get(this).age
}
return Person;
}());