《红宝书》 |Map与WeakMap

Map

在ES6以前,JavaScript通过Object来实现"键值"的储存,但这种方式有一定的问题。Map是一种新的集合类型,也称映射。它带来了真正的"键值"储存机制。Map的大多数特性可由Object类型实现,但两者有细微的差距。

实例化

let m1=new Map()

//接收一个数组作为参数,该数组元素是表示键值对的数组
let m2=new Map([
    ["key1","value1"],
    ["key2","value2"],
    ["key3","value3"]
])

基本API

  • set(key,value):添加键/值对
  • get(key):接受键名,获取对应值
  • has(key):接受键名,判断是否存在该键/值对
  • delete(key):接受键名,删除指定键/值对
  • clear():清空实例中所有键/值对
  • 属性size:获取映射中键值对的数量
    const m=new Map()
    m.has("name")         //false
    m.get("name")         //undefined
    m.size                //0
    
    m.set("name","Matt")  //添加键/值
    
    m.has("name")         //true
    m.get("name")         //Matt
    m.size                //1
    
    m.delete("name")      //删除键名为name的键/值对
    m.clear()             //清空m的键/值对
    
    set()会返回映射实例,因此可以把多个操作连起来
    const m=new Map()
    m.set("name","Matt")
     .set("age",16)
    
    //对同一个键多次赋值,后面的值将覆盖前面的值
    m.set("name","Matt")
     .set("name","Amy")
    

映射特性

与Object只能使用数值、字符串或符号作为键不同,Map的键与值可以使用任何JavaScript类型作为键。

const m=new Map()
const functionKey=function(){}
const symbolKey=Symbol()
const objectKey=new Object()
m.set(functionKey,"functionValue")
m.set(symbolKey,"symbolValue")
m.set(objectKey,"objectValue")

只要内存地址不同,就视为两个键:

m.get(functionKey)  //functionValue
m.get(function(){}) //undefined

当映射的键/值为引用类型时,若对其内容或属性修改,映射本身保持不变:

const m=new Map()
const objKey={}
const objValue={}
m.set(objKey,objValue)

//修改键/值对的内容或属性
objKey.name="Matt"
objValue.hobby="read"
//依然可以获取
m.get(objKey)       //{hobby:read}

迭代应用

  • entries():返回键/值的迭代器。(可用Symbol.iterator属性代替)

  • keys():返回键的迭代器

  • values():返回值的迭代器

  • forEach():遍历map的所有成员

    let m=new Map([
        ["key1","value1"],
        ["key2","value2"],
        ["key3","value3"]
    ])
    
    for(let item of m.entries()){
        console.log(item)
    }
    //["key1","value1"]
    //["key2","value2"]
    //["key3","value3"]
    
    for(let item of m[Symbol.iterator]()){
        console.log(item)
    }
    //["key1","value1"]
    //["key2","value2"]
    //["key3","value3"]
    
    for (let [key, value] of m) {
      console.log(key, value);
    }
    //key1,value1
    //key2,value2
    //key3,value3
    
    for(let item of m.keys()){
        console.log(item)
    }
    //key1
    //key2
    //key3
    
    for(let item of m.values()){
        console.log(item)
    }
    //value1
    //value2
    //value3
    
    m.forEach((val,key)=>console.log(`${key}->${val}`))
    // key1->value1
    // key2->value2
    // key3->value3
    

    与Object的主要差异是,Map实例会维护键/值对的插入顺序

转换类型

Map转为数组

通过扩展运算符(...)或Array.from()实现转换:

let map = new Map([
    ["key1","value1"],
    ["key2","value2"],
    ["key3","value3"]
]);

console.log([...map])           //[["key1","value1"],["key2","value2"],["key3","value3"]]
console.log([...map.entries()]) //[["key1","value1"],["key2","value2"],["key3","value3"]]
console.log([...map.keys()])    //["key1","key2","key3"]
console.log([...map.values()])  //["value1","value2","value3"]
console.log(Array.from(map))    //[["key1","value1"],["key2","value2"],["key3","value3"]]

结合数组的map方法、filter方法,可以实现Map的遍历和过滤:

let m1=new Map().set(1,"one").set(2,"two").set(3,"three")
let m2=new Map(
    [...m1].filter(([key,value])=>key<3)
)
let m3=new Map(
    [...m1].map(([key,value])=>[key+1,value+"*"])
)

console.log(m1) //Map(3) {1 => "one", 2 => "two", 3 => "three"}
console.log(m2) //Map(2) {1 => "one", 2 => "two"}
console.log(m3) //Map(3) {2 => "one*", 3 => "two*", 4 => "three*"}

Map转为对象

前提是键为字符串形式:

function mapToObj(map) {
  let obj = Object.create(null);
  for (let [k,v] of map) {
    obj[k] = v;
  }
  return obj;
}

let m = new Map().set("name", "Matt").set("age", 16);
mapToObj(m) //{name: "Matt", age: 16}

对象转为Map

function objToMap(obj) {
  let map = new Map();
  for (let k of Object.keys(obj)) {
    map.set(k, obj[k]);
  }
  return map;
}

objToMap({name: "Matt", age: 16})   //Map(2) {"name" => "Matt", "age" => 16}

选择Object还是Map

  • 内存占用:储存单个键/值对所占用的内存数量会随着键的数量线性增加,批量添加或删除键/值对取决于个浏览器对该类型内存分配的工程。给定固定大小的内存,Map大约比Object多储存50%
  • 插入性能:在所有浏览器中,向Map插入键/值对要比向Object插入稍微快一些。如果涉及大量插入操作,Map效果更佳
  • 查找速度:Object效率更高
  • 删除性能:如果涉及大量删除操作,Map的delete()效率更高

WeakMap

WeakMap称为弱映射。WeakMap是Map的兄弟类型,其API也是Map的子集。WeakMap中的weak描述的是JavaScript垃圾回收程序对待弱映射中的键的方式。

实例化

const wm=new WeakMap()

弱映射特性

弱映射中的键只能是Object或者是继承自Object的类型,否则会抛出TypeError错误。值的类型没有限制。

const key1={id:1},key2={id:2},key3={id:3}
const wm=new WeakMap([
  [key1,"val1"],
  [key2,"val2"],
  [key3,"val3"]
])

初始化是全有全无的操作,只要有一个键不符合要求,那么就初始化失败:

const key1={id:1},key2={id:2},key3={id:3}
const wm=new WeakMap([
  [key1,"val1"],
  ["key2","val2"],
  [key3,"val3"]
])

原始值可先转换为原始包装类型再作为键:

const strKey=new String("key1")
const wm=new WeakMap([
  [strKey,"val1"]
])

与Map相比,WeakMap是不可迭代的。

基本API

  • set(key,value):添加键/值对
  • get(key):接受键名,获取对应值
  • has(key):接受键名,判断是否存在该键/值对
  • delete(key):接受键名,删除指定键/值对

与Map相比,少了clear()size属性。

posted @ 2021-02-15 00:30  sanhuamao  阅读(120)  评论(0编辑  收藏  举报