ES6 Map:JavaScript开发者必须掌握的数据结构

118 阅读5分钟

在ES6中新增了两种数据结构分别是Map和WeakMap数据结构,下面就先来深度了解一下Map数据结构。

为什么会出现Map

我们知道Map和传统的对象使用的都是键值对的形式,但是传统意义上对象的键只能是字符串,无论将任何数据类型当做键都可能会报错或者被转换为字符串形式,正是由于这一限制ES6提出了Map数据结构。

Map是什么

Map可以看作是对象的升级,它的键可以是任意类型的值不限于字符串,也就是说Object对象结构提供的是“字符串=》值”的对应,而Map数据结构提供的是“值=》值”的对应。

image.png

Map是一个可迭代对象因为它具有可迭代协议或者说接口,在它的原型属性中具有Symbol.iterator属性,不清楚这个知识点的同学可以参考这篇文章传送门。所以它也是可以使用for of进行遍历操作的。

创建Map的两种方式

第一种可以通过实例化Map构造函数的方式来创建,完成实例的创建后可以调用对象上的一些方法进行增删改查操作。

    const m = new Map();
    
    // 使用set的方法设置键值对
    m.set("a", 1);
    m.set({ b: 2 }, 2);

当我们想要获取一个由非基本类型的键时可以通过声明一个变量保存其引用,然后使用get方法进行获取操作。

    const obj = {
        b:2
    };
    
    const m = new Map();
    m.set(obj,2);
    m.get(obj); // 2

使用has方法判断是否有该属性和delete方法删除跟上面也是一样的。

第二种就是通过给Map的构造函数传递参数的方式来创建。

    let obj={
        b: 2 
    };
    let m = new Map([
        ['a',1],
        [obj,2]
    ]);

因为创建出来的Map是一个可迭代的数据类型所以接受的是一个数组类型,里面为什么是二维数组呢,这是因为要保存键值对的原因,在该数组中每一个子数组都表示一个键值对,这样一来就可以表达多个键值对并将二维数组中的第一个元素作为key,第二个元素作为value。其实类似于下面的做法:

const items = [
    ['a',1],
    ['b',2]
];
const m = new Map(items);
items.forEach([key,value]=>m.set(key,value));

注意点

在对象中存在同名属性覆盖的问题,在Map中也存在,对于基本数据类型后者会覆盖前者,对于引用类型来说只有是同一个对象的引用Map才会视为是同一个键。

    let m = new Map();
    m.set("a",1);
    m.set("a",2) ;
    m.get("a"); // 2
    
    const map = new Map()
    const k1 = ['a'];
    const k2 = ['a'];

    map.set(k1, 111);
    map.set(k2, 222);
    m.get(k1); // 111
    m.get(k2); // 222

此外还可以理解为只要两个值严格相等Map就视为同一个键,所以还需要注意这些情况:

    const m = new Map();
    m.set(+0,1);
    m.get(-0); // 1  +0 不严格相等于 -0
    
    m.set(NaN,0);
    m.get(NaN); // 0 特别需要注意:Map将NaN视为同一个键

Map和Object的区别

  • Map的键可以是任意类型,Object的键只能是字符串或Symbol类型
  • Map是有序的可迭代的,Object是无序的不可迭代的
  • Map通过size属性描述长度,Object通过Object.keys().length等方法来描述
  • Map没有序列化操作即JSON.stringify/JSON.parse方法,Object可以被序列化(里面必须是纯数据)

WeakMap

WeakMap与Map结构相似,也是用来生成键值对的集合,不同的是对键名的引用属于弱引用不计入垃圾回收机制,Map属于强引用,所以在此之前先了解一下引用一词。

引用

比如现在声明了一个对象,它的存储结构是这样的

let obj = {
    a:1
};
obj = null; // 只是将其引用给断掉了,源对象可能还会存在

image.png

上图表示将存放在堆内存中的对象赋值给obj变量,因此在obj和对象之间存在着一个引用关系,此时垃圾回收机制中的引用计数会加1表示该对象还被引用着不应该被回收,当没有变量保存该引用也就是说当计数为0时会被当做垃圾回收掉,但是需要注意回收的时机是不确定的也就是说什么时候会被回收是不确定的。

说完引用的概念之后再来看WeakMap,WeakMap的键是弱引用,也就是说WeakMap中键名指向的对象不会计入垃圾回收机制计数,它的目的就是为了自动删除引用,只要所引用的对象的其他引用都被清除(设为null等)垃圾回收机制就会释放该对象所占用的内存,也就是说,里面的键名对象和所对应的键值对会自动消失不需要再手动删除引用。

    let obj = {
        a: 1
    }
    const m = new WeakMap();
    m.set(obj, 2)
    console.log(m) // 空键值对
    obj = null

但是需要注意WeakMap弱引用的只是键名而不是键值,键值依然是正常引用。

与Map的区别

WeakMap和Map的区别主要是WeakMap没有keys、values、entries、clear方法,因为弱引用的原因某个键名是否存在是不确定的,所以为了防止出现这种不确定性就统一规定不能取到键名,它只有四个方法可以使用分別是get、set、has、delete

最后

上面这些应该可以说只是皮毛了,目前只是先把基础巩固一下,后面会在此基础上继续进行扩展。