• 《红宝书》 |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属性。

  • 相关阅读:
    项目经理-要材料
    java stream 处理
    listGiftBagShowInfo 这个方法 我搞了一晚上
    BigDecimal.ROUND_UP 如果 从 double 到 Decimal 有异常, 必须从double到String然后 Decimal 就可以了
    json串 转 list<class> 方法 List转JSONArray和JSONArray转List
    昨晚加班到3点多-身体都虚脱了
    MVN 点击compile总是失败
    IDEA使用笔记(八)——自动生成 serialVersionUID 的设置
    Maven 版本号规范
    java多线程学习
  • 原文地址:https://www.cnblogs.com/sanhuamao/p/14403090.html
Copyright © 2020-2023  润新知