Map
可以用于存储键值对的数据。在ECMAScript6
之前为了达到这种效果都是使用的Object
对象。Map
能够记住原始插入顺序,任何值(对象或原始值)都可作为键或值。
Map
基本API
可以通过new
关键字和Map
构造函数创建一个空的映射
const stringKey = 'key1'
const numberKey = 1
const symbolKey = Symbol('key')
//创建一个空的映射
const map = new Map()
//使用.size属性查看长度
console.log(map.size) //0
//查看是否有指定键对应的值
console.log(map.has(stringKey)) //false
//根据键返回值
console.log(map.get(stringKey)) //undefined
//使用.set添加值
map.set(stringKey, 'stringValue')
//允许链式调用
map.set(numberKey, 'numberValue').set(symbolKey, 'symbolValue')
//查看长度
console.log(map.size) //3
//根据键返回值
console.log(map.get(stringKey)) //"stringKey"
console.log(map.get(numberKey)) //"numberValue"
console.log(map.get(symbolKey)) //"symbolValue"
//打印map实例
console.log(map) //Map(3) {"key1" => "stringValue", 1 => "numberValue"} //可以发现Symbol的键没有打印出来,但是长度还是3,展开后是这样的,也能通过.get得到值
//0: {"key1" => "stringValue"}
//1: {1 => "numberValue"}
//2: "symbolValue"
//删除数据对
map.delete(numberKey)
console.log(map.has(numberKey)) //false
//清空map
map.clear()
console.log(map.size) //0
//也可以创建实例时就初始化
//接收一个可迭代的对象,需要包含键/值对数组, 可以使用任何 JavaScript 数据类型作为键
const initMap = new Map([
[stringKey, 'stringValue'],
[numberKey, 'numberValue'],
[symbolKey, 'symbolValue']
])
console.log(initMap) //Map(3) {"key1" => "stringValue", 1 => "numberValue"}
Map
内部使用SameValueZero
比较操作(ECMAScript
规范内部定义,语言中不能使用),基本上相当于使用严格对象相等的标准来检查键的匹配性,但比较也可能导致意想不到的冲突
const a = 0 / ''
const b = 0 / ''
const pz = +0
const nz = -0
const map = new Map([
[a, '这是a的值'],
[pz, '这是+0的值']
])
console.log(map.get(b)) //这是a的值
console.log(map.get(nz)) //这是+0的值
Map的顺序与迭代
Map
会维持键值对的插入顺序
由于实现了iterator
迭代器,可以使用values, keys, entries
方法,会返回迭代器。
const map = new Map([
['key1', 'value1'],
['key2', 'value2']
])
//for...of返回[key, value]形式
for (const pair of map) {
console.log(pair)
}
//["key1", "value1"]
//["key2", "value2"]
//Map.prototype.entries()返回[key, value]形式
for (const pair of map.entries()) {
console.log(pair)
}
//["key1", "value1"]
//["key2", "value2"]
//Symbol.iterator属性返回[key, value]形式
for (const pair of map[Symbol.iterator]()) {
console.log(pair)
}
//["key1", "value1"]
//["key2", "value2"]
//Map.prototype.keys()返回键
for (const k of map.keys()) {
console.log(k)
}
//"key1"
//"key2"
//Map.prototype.values()返回值
for (const v of map.values()) {
console.log(v)
}
//"value1"
//"value2"
//Map.prototype.forEach()
map.forEach(function (v, k) {
console.log(k, v)
})
//"key1" "value1"
//"key2" "value2"
//支持扩展操作
console.log(...map)//["key1", "value1"] ["key2", "value2"]
//可以方便的复制或合并Map
//复制
const clone = new Map(map)
console.log(clone.get('key1'))
console.log(clone === map) //false
//合并
const anotherMap = new Map([
[12, '12'],
[34, '34']
])
const merged = new Map([...map, ...anotherMap])
console.log(merged.size) //4
键和值在迭代器遍历时是可以修改的,但映射内部的引用则无法更改
const m = new Map([
['key1', 'value1']
])
//作为键的字符串原始值是不能修改的
for (let k of m.keys()) {
k = 'key2'
console.log(k) //"key2"
console.log(m.get('key1')) //"value1"
}
WeakMap
WeakMap
中的"weak"
(弱)描述的是JavaScript
垃圾回收程序对待”弱映射“中的键的方式。其键必须是对象,否则会报错。由于这种弱引用,WeakMap
的key
是不可枚举的。
const wm = new WeakMap()
const objectKey1 = {}
const objectKey2 = {key: '1'}
wm.set(objectKey1, 'value1')
.set(objectKey2, 'value2')
console.log(wm.has(objectKey1)) //true
console.log(wm.get(objectKey1)) //'value1'
弱键
WeakMap
中的键,只要没有引用再指向这个键,就将会被垃圾回收程序回收
const wm = new WeakMap()
wm.set({}, 'value') //由于这个对象没有引用指向它,会被当做垃圾回收
//另一个例子
const container = {
key: {}
}
const wm2 = new WeakMap()
wm2.set(container.key, 'value')
console.log(wm2.has(container.key)) //true
container.key = null //执行这个以后这个键值对将会被垃圾回收程序清理掉了
既然都已经没有引用指向这个对象了,当然再用
wm2.has(container.key)
也没有意义,container.key
指向的已经不是原来那个对象了