ES6新增引用类型:Map、WeakMap、Set、WeakSet

1,052 阅读5分钟

前言

Map、WeakMap、Set、WeakSet作为es6新增的引用数据类型,平常用的最多的无非使用Set去重,但对于Map、WeakMap、WeakSet的用法及应用场景都很模糊,这里通过综合红宝书及MDN文档从:简单用法、使用场景、区别。三方面记录一下这四种引用类型的使用和差异。

Map

Map: es6新增特性,是一种新的集合类型,一种“键/值”存储的对象。

初始化:可以创建的时候赋值,也可以通过它内置的API:set()方法进行添加键值对。举例: 初始化赋值。

const m1 = new Map([
    ['key1', 'value1'],
    ['key2', 'value2']
])

Map提供了很多方法简单举例:(增删查)

增:set()

set():赋值.

image.png

返回的是映射实例,因此可以实现链式操作

m1.set(['key3', 'value3']).set(['key6', 'value6'])

删:delete(),clear()

delete()

移除对应的键的键值对。 存在相同键值的键值对都会被删除。

m1.delete(key)

clear()

移除Map对象中的所有元素

m1.clear() 

查:get(),has()

get()

返回的是Map对象中对应键的值

m1.get(key)

has()

返回的是Map对象中是否存在对应的键 布尔值

举例:

const m2 = new Map([[]])
m2.has(undefined) //true
m2.get(undefined) //undefined

获取数量:size()

获取键值对数量

m1.size()

顺序与迭代:entries()

Map会维护键值对的插入顺序,因此可以根据插入顺序进行迭代.Map提供entries()来引用Symbol.iterator。 有图有真相:

image.png

提到这里Object是没有Symbol.iterator的所以,不可使用迭代器。知识点:for offor in的区别:链接

迭代键值:keys(), values()

keys()

MDN: 返回一个引用的 Iterator 对象。它包含按照顺序插入 Map 对象中每个元素的key值.

values()

MDN: 返回一个新的Iterator对象。它包含按顺序插入Map对象中每个元素的value值.

keys()和values()都是引用iterator对象,iterator的返回结果包含 value和done属性.这里注意keys和values使用next()后获取的值都是value

image.png

WeakMap(弱映射)

es6新增特性,也是一种新的集合类型。是Map的”兄弟“类型,其API也是Map的子集。

红宝书中定义:week-> ‘弱弱的拿着’,意思是这些键不属于正式引用,不会被垃圾回收。我认为就像涉及到闭包一样,使用了引用值,因此只要键存在,就存在于映射中,对此不会被当作垃圾回收。 举例:

const wm = new WeekMap();
wm.set({}, 'val');

上面的代码中通过set()初始化一个键为空对象的,由于没有指向这个空对象的引用,对此这里会被当做垃圾回收。

通过下面这个例子就可以更能清楚“不是被正式的拿着,不会阻止垃圾回收”

const wm = new WeakMap();
const container = {
    key: {}
}
wm.set(container.key, 'val');
function removeReference() {
    container.key = null;
}

这里初始化的时候通过一个对于引用空对象的键名,是一个引用(指向container的key值)所以不会被当作垃圾回收。 而可以通过调用removeReference这个函数清空引用值,最后达到垃圾回收的效果。(闭包的解决方案之一也是将引用值设为null

提供的方法:

image.png

从WeakMap实例对象看到,并没有提供可迭代方法。也正因为‘weak’,没有正式的引用。

其余方法的具体用法见:MDN

Set

ES6新增集合类型。Set中的元素只会出现一次,即 Set 中的元素是唯一

增 add()

Set对象尾部添加一个元素。返回该Set对象。返回的是集合实例,因此可以通过实现链式操作(Map的set()同理)

const s = new Set()
s.add('val1').add('val2').add('val3')

删 delete(), clear()

delete()

删除当前value值 返回值为布尔值

s.delete(value)

clear()

清空Set对象中的所有元素

s.clear()

查 has()

查询是否拥有当前value 返回值为布尔值

s.has(value)

WeakSet(弱集合)

WeakSet和Set和WeakMap和Map介绍相同,上面已经详细介绍过了。

其用法可以参考MDN

WeakMap的使用场景

1. 私有变量

之前我一篇文章也讲到过js的封装、继承、多态。和这里思路类似。

代码摘自红宝书:

const User = (() => {
    const wm = new WeakMap();
    class User {
        constructor(id) {
            this.idProperty = Symbol('id');
            this.setId(id);
        }
        setPrivate(property, value) {
            const privateMembers = wm.get(this) || {};
            privateMembers[property] = value;
            wm.set(this, privateMembers)
        }
        getProvate(property) {
            return wm.get(this)[property]
        }
        setId(id) {
            this.setPrivate(this.idProperty, id)
        }
        getId(id) {
            return this.getProvate(this.idProperty)
        }
    }
    return User
})()
const user = new User(123)
console.log(user.getId()) //123
user.setId(456)
console.log(user.getId()) //456

实现: 以对象实例为键,以私有成员的字典为值。通过上面方式:获取不到弱映射的键名,所以无法取得对应的值。最主要还是利用了Symbol对象。

2. DOM节点元数据

WeakMap实例不会阻止垃圾回收机制,若想对一个DOM节点关联一些样式,那么我们将DOM节点设为键名,当节点被删除掉(引用地址为null),那么这里就会采用垃圾回收机制,释放内存

Set的使用场景

1. 数组去重 es6拓展运算符配合new Set()

var arr = [1, 2, 3, 2, 1];
var result = [...new Set(arr)]
console.log(result) // [1, 2, 3]

2. 数组去重 Array.from配合new Set()

Array.from()  方法从一个类似数组或可迭代对象创建一个新的,浅拷贝的数组实例。

var arr = [1, 2, 3, 2, 1];
var result = Array.from(new Set(arr))
console.log(result) // [1, 2, 3]

Map和Object的区别

  1. Map为体现键值对形式的数据结构(Map可以支持任意JS数据类型作为键名,而Object只能是数值字符串或者符号
  2. 内存占用: 不同浏览器下情况不同,Map大约比Object多存储50%的键值对
  3. 插入性能: 插入速度都不会因为键值对的数量进行线性增加,但对于大量插入操作,Map的性能更佳。
  4. 查找速度: 如果是少量的键值对,Object的速度更快
  5. 删除性能: Map在大多数浏览器引擎中删除操作较快。

总结

  1. es6新增引用数据类型:Map、WeakMap、Set、WeakSet
  2. Map、WeakMap: 键值对映射
  3. Set、WeakSet:的集合
  4. 带有weak的 弱引用,不阻止垃圾回收机制、不可迭代
  5. 对于WeakMap、WeakSet都可以在对DOM节点进行修饰时,作为存储类型。

往期js文章:

面试题

深浅拷贝方法总结、练习

几分钟了解js的模块化、IFEE

JavaScript实现封装、继承、多态