Map

6 篇文章 0 订阅

Map

含义和基本用法

JavaScript的对象(Object),本质上是键值对的集合(Hash结构),但传统上只能用字符串当做键。
ES6提供了Map数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当做键。Object提供的是“字符串-值”的对应Map结构提供了“值-值”的对应,是一种更完善的Hash结构对应。

      const m = new Map()
      const o = { p: 'hello' }

      m.set(o, 'content')
      console.log(m)

      const map = new Map([['name', '张三'], ['title', 'author']])
      //实际进行的操作
      const items = [['name', '张三'], ['title', 'author']]
      const map2 = new Map()

      items.forEach(([key, value]) => map2.set(key, value))

      console.log(map, map2)

在这里插入图片描述
不仅仅是数组,任何具有Iterator接口、且每个成员都是一个双元素的数组的数据结构都可以当做Map构造函数的参数——Set和Map都可以用来生成新的Map。

  	  const set = new Set([['foo', 1], ['bar', 2]])
      const m1 = new Map(set)
      const m2 = new Map([['baz', 3]])
      const m3 = new Map(m2)
      console.log('m1:', m1, 'm2:', m2, 'm3:', m3)

在这里插入图片描述
如果获取一个未知的键,返回undefined
只有对同一个对象的引用,Map结构才会将其视为同一个键

      const map = new Map()
      map.set(['a'], 555)
      console.log(map.get(['a']))

在这里插入图片描述
这里的set和get是两个不同的数组实例,内存地址是不一样的,所以get方法无法获取该键返回undefined
Map的键是一个简单类型的值(数字,字符串,布尔值),只要两个值严格相等,Map将其视为一个键,比如0和-0是一个键,布尔值的true和字符串的true是两个不同的键。另外undefined和null也是两个不同的键,虽然NaN不严格等于自身,但Map将其视为同一个键

Map结构的实例的属性和操作方法
  1. size:返回Map结构的成员数
  2. Map.prototype.set(key,value)
    set方法设置键名key对应的键值为value,然后返回整个Map结构。如果key已经有值,则键值会被更新,否则就生成该键
    set方法返回的是当前Map对象,因此可采用链式写法
  3. Map.prototype.get(key)
    get方法读取key对应的键值,如果找不到key,则返回undefined
  4. Map.prototype.delete(key)
    delete方法删除某个键,返回true。如果删除失败,返回false
  5. Map.prototype.clear()
    clear()方法清除所有成员,没有返回值
遍历方法

Map结构原生提供三个遍历器生成函数和一个遍历方法

  • Map.prototype.keys():返回键名的遍历器
  • Map.prototype.values(): 返回键值的遍历器
  • Map.prototype.entries():返回所有成员的遍历器
  • Map.prototype.forEach():遍历Map的所有成员‘

Map的遍历顺序就是插入顺序
Map结构的默认遍历器接口(Symbol.interator属性)就是entires方法

结合数组的map方法、filter方法,可以实现Map的遍历和过滤(Map本身没有map和filter方法)

      const map0 = new Map()
        .set(1, 'a')
        .set(2, 'b')
        .set(3, 'c')

      const map1 = new Map([...map0].filter(([k, v]) => k < 3))
      const map2 = new Map([...map0].map(([k, v]) => [k * 2, '_' + v]))
      console.log('map0:', map0)
      console.log('map1:', map1)
      console.log('map2:', map2)

在这里插入图片描述
Map有个forEach方法与数组的forEach类似
forEach可以接受第二个参数,用来绑定this

      let map = new Map()
        .set(1, 'a')
        .set(2, 'b')
        .set(3, 'c')
      const reporter = {
        report: function(key, value) {
          console.log('key: %s,Value: %s', key, value)
        }
      }
      map.forEach(function(value, key, map) {
        this.report(key, value)
      }, reporter)

在这里插入图片描述

与其他数据结构的相互转换

’1. Map转成数组最方便的方法:扩展运算符(…)

 	 const myMap = new Map().set(true, 7).set({ foo: 3 }, ['abc'])
      let arr = [...myMap]
      console.log(arr)

在这里插入图片描述
2. 数组转成Map

    let map = new Map([[true, 7], [{ foo: 3 }, ['abc']]])
 	console.log(map)

在这里插入图片描述
3. Map转成对象
如果所有Map的键都是字符串,则可以无损地转为对象,但若存在非字符串的键名,这个键名会先转成字符串,再作为对象的键名。

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

      const myMap = new Map().set('yes', true).set(false, false)
      let res = strMapToObj(myMap)
      console.log(res)

在这里插入图片描述
4. 对象转为Map

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

objToStrMap({yes: true, no: false})
  1. Map转为JSON
    1. Map键名都是字符串,转成对象JSON
    2. Map键名有非字符串,可以选择转成数组JSON
      function strMapToObj(strMap) {
        let obj = Object.create(null)
        for (let [k, v] of strMap) {
          obj[k] = v
        }
        return obj
      }

      function strMapToJson(strMap) {
        return JSON.stringify(strMapToObj(strMap))
      }

      function mapToArrayJson(map) {
        return JSON.stringify([...map])
      }

      let myMap1 = new Map().set('yes', true).set('no', false)
      let toJson = strMapToJson(myMap1)
      let myMap2 = new Map().set(true, 7).set({ foo: 3 }, ['abc'])
      let toArray = mapToArrayJson(myMap2)
      console.log(toJson, toArray)

在这里插入图片描述
6. JSON 转成Map
JSON转成Map,键名都是字符串

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

      function jsonToStrMap(jsonStr) {
        return objToStrMap(JSON.parse(jsonStr))
      }

      let myMap = jsonToStrMap('{"yes": true, "no": false}')

      console.log(myMap)

在这里插入图片描述
如果整个JSON就是一个数组,且每个数组成员本身,又是一个有两个成员的数组,那么可以转为Map。

      function jsonToStrMap(jsonStr) {
        return new Map(JSON.parse(jsonStr))
      }

      let myMap = jsonToStrMap('[[true,7],[{"foo":3},["abc"]]]')
      console.log(myMap)

在这里插入图片描述

WeakMap

WeakMap与Map的区别:

  1. WeakMap只接受对象作为键名
  2. WeakMap的键名所指向的对象,不计入垃圾回收机制

WeakMap的键名所引用的对象都是弱引用——垃圾回收机制不考虑在内(只要所引用的对象的其他引用被清除,来及回收机制会释放对该对象所占用的内存)

适用场所

其键对应的对象可能在将来会消失。此结构有助于防止内存泄漏

语法

有4个方法可用:

  • get()
  • set()
  • has()
  • delete()
示例
// 手动执行一次垃圾回收,保证获取的内存使用状态准确
> global.gc();
undefined

// 查看内存占用的初始状态,heapUsed 为 4M 左右
> process.memoryUsage();
{ rss: 21106688,
  heapTotal: 7376896,
  heapUsed: 4153936,
  external: 9059 }

> let wm = new WeakMap();
undefined

// 新建一个变量 key,指向一个 5*1024*1024 的数组
> let key = new Array(5 * 1024 * 1024);
undefined

// 设置 WeakMap 实例的键名,也指向 key 数组
// 这时,key 数组实际被引用了两次,
// 变量 key 引用一次,WeakMap 的键名引用了第二次
// 但是,WeakMap 是弱引用,对于引擎来说,引用计数还是1
> wm.set(key, 1);
WeakMap {}

> global.gc();
undefined

// 这时内存占用 heapUsed 增加到 45M 了
> process.memoryUsage();
{ rss: 67538944,
  heapTotal: 7376896,
  heapUsed: 45782816,
  external: 8945 }

// 清除变量 key 对数组的引用,
// 但没有手动清除 WeakMap 实例的键名对数组的引用
> key = null;
null

// 再次执行垃圾回收
> global.gc();
undefined

// 内存占用 heapUsed 变回 4M 左右,
// 可以看到 WeakMap 的键名引用没有阻止 gc 对内存的回收
> process.memoryUsage();
{ rss: 20639744,
  heapTotal: 8425472,
  heapUsed: 3979792,
  external: 8956 }
用途

DOM节点作为键名,布置私有属性

//作为键名
let myElement = document.getElementById('logo');
let myWeakmap = new WeakMap();

myWeakmap.set(myElement, {timesClicked: 0});

myElement.addEventListener('click', function() {
  let logoData = myWeakmap.get(myElement);
  logoData.timesClicked++;
}, false);

//布置私有属性
const _counter = new WeakMap();
const _action = new WeakMap();

class Countdown {
  constructor(counter, action) {
    _counter.set(this, counter);
    _action.set(this, action);
  }
  dec() {
    let counter = _counter.get(this);
    if (counter < 1) return;
    counter--;
    _counter.set(this, counter);
    if (counter === 0) {
      _action.get(this)();
    }
  }
}

const c = new Countdown(2, () => console.log('DONE'));

c.dec()
c.dec()
// DONE
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值