ES6的Map和Set的使用,以及weakMap的一点理解

更新日期: 2021-05-19阅读: 1.2k标签: es6

一、Map

Map可以用来存储键值对,在一定程度上扩展了Object的内容。


1. Map的基本api

创建新的Map实例

// 创建一个空映射
let map = new Map()  
// 传入一个包含键值对组的可迭代对象,
// 传入的可迭代对象会按顺序插入到新的map实例里
let arr = [
    ['key1', 'val1'],
    ['key2', 'val2'],
    ['key3', 'val3']
]
let map1 = new Map(arr) 
// 同样也可以是自定义的迭代对象

set(key, value) 添加新的键值对,返回一个一个映射实例(所以可以链式操作)

get(key) 根据键名返回对应的键值

size属性,返回当前存储的键值对数量

has(key) ,返回是否有当前传入的键名是否存在,返回boolean类型

delete(key), 删除指定的键值对

clear() 清空映射,删除全部的键值对

let m = new Map()

// 添加新的键值对
m.set('name', 'zhangsan')
 .set('age', 20)

// 获取对应的键值
console.log(m.get(name));

// 确定键名是否在实例上
console.log(m.has('name'));

// 确定存储的键值对的数量

console.log(m.size);

// 删除指定的键值对
m.delete('name')

// 清空映射,删除全部的键值对
m.clear()

Map的一些迭代方法

在插入内容时,map实例会维护插入顺序,遍历出来的顺序是插入顺序,而object的遍历顺序可能不是插入顺序。所以下面三个方法作用是合object中一样的,只是map调用会按照键值对的插入顺序迭代

entries 与迭代map自身相同,map本身也是可迭代的

keys
values
不同于对象的方法

forEach 不同于数组的forEach方法,map的forEach方法回调函数里面的两个参数一个是value,一个是key,函数的第二个参数同数组方法,是回调函数内部this的值

let m = new Map([
  ['key1', 'value1'],
  ['key2', 'value2'],
  ['key3', 'value3']
])

m.forEach((value, key) => {
  console.log(value, key);
})
// value1 key1
// value2 key2
// value3 key3

Map解决了Object的什么问题

Object只能使用数字(number),字符串(string),符号(Symbol),作为键名,Map可以使用任何类型作为建名。两者之间相同的是对键值都没有限制,object在用其他类型作为键值时会toString进行转换。所以当需要存储更多类型时,选用Map


选择Object和Map

内存方面

同样大小的内存,map比Object存的多,所以Map胜, 2. 插入时 插入时两种方式差不多,插入大量数据时,还是选择map 3. 查找速度 差异极小,使用连续整数作为属性时,调用浏览器可以进行优化,可能速度会高点 4. 删除性能 删除时,map更优


Set

基本api

创建新的Set实例

//  创建一个空集合
let s = new Set()

// 传入可迭代对象,包含插入到集合中的新元素
let s = new Set([1, 2, 3])

add(item) 添加新元素,类似map的set方法,返回一个新的实例,所以可以链式调用

has(item) 查询是否有指定元素

size 属性, 查看集合的元素数量

delete(item) 删除指定的元素,返回布尔值,表示是否存在要删除的元素

clear() 清除集合,删除所有元素


迭代

set在插入时也会维持顺序,按插入顺序迭代

keys()

values()

自身可迭代

三种操作结果相同,都是迭代出每个元素

for(let i of s.values()) {
  console.log(i);
}
for(let i of s.keys()) {
  console.log(i);
}
for(let i of s) {
  console.log(i);
}

entries,产生包含两个集合中重复元素的数组

let s = new Set([1, 2, 3])

for(let i of s.entries()) {
  console.log(i);
}

// [1, 1]
// [2, 2]
// [3, 3]

关于weakMap的理解,

weakMap根据名字就可以看出来,它是一种弱的map映射关系,即WeakMaps保持了对键名所引用的对象的弱引用,弱引用就是在任何时候都有可能被垃圾回收机制回收掉的引用。直接声明的对象如

let o = {} // 这样创建一个对象是强引用,强引用是不会被垃圾回收的
// 只有在设置了null时,才会被回收
o = null // 可以被垃圾回收了

当声明了一个对象,而Map又引用这个对象作为键名时

let o = {name: 'zhangsan'}

let m = new Map()

// map强引用了o作为键值
m.set(o, 1) 

// 当设置o= null 时,只是清掉了o对对象的引用
// 但是m仍然引用着对象,所以并不能清除

// 想要被清除掉,首先得先delete(key) 然后在使用key= null,才能清除

由此,weakMap的作用就显现出来了,由于它的键值对是弱引用,若依,当对象key=null时,因为他本来键值就是弱引用,所以等到下一轮垃圾回收执行时,该引用对象就会被回收掉. 所以weakmap的作用就是保留了对键名所引用的对象的弱引用,即,如果键名所引用的对象不被其他变量引用,那么垃圾回收就会释放掉对象所占内存,因为弱引用随时都有可能被垃圾回收清除,垃圾回收机制执行实际不可预测,所以WeakMap也就不能够遍历,简单的例子来说明

当weakMap的键值没有被引用时

let wm = new WeakMap()
// 空对象没有被任何值引用,只是作为了键值
// 所以,直接会被垃圾回收掉,键值对被破坏,值本身也会被垃圾回收
wm.set({}, 1)
console.log( wm.get({})) // undefined

当值有被引用时

let wm = new WeakMap()

// 创建一个对象引用着key
let obj = {
  key: {}
}
// 在实例里用key作为键值
wm.set(obj.key, 1)

// 可以访问该键值对
console.log(wm.get(obj.key));  // 1

// 当清除掉obj对key的引用时,在wm中也就没有了对应的键值对映射
obj.key = null
console.log(wm.get(obj.key)); // undefined

WeakMap的四个方法,

get()
set()
has()
delete()

Weak中的建只能是对象


WeakMap的应用场景

1. 数据缓存

当我们需要在不修改原对象的情况下存储某些属性或者根据对象存储一些计算的值,而又不想关系这些值是否被回收掉时,可以使用WeakMap

// 比如说保存对象属性的长度的缓存
const cache = new WeakMap()

function keyLength(obj) {
  // 如果当前缓存中有这个数据就直接返回
  if(cache.has(obj)) {
    return cache.get(obj)
  } else {
    // 如果没有,就把当前对象的键的长度存在缓存中
    const count = Object.keys(obj).length
    cache.set(obj, count)
    // 返回这次计算的长度
    return count
  }
}

2. dom中的数据

有时候可能会对dom节点关联一些数据,比如是否禁用什么的,可以用映射使他们关联起来,但是如果有一些操作可能需要删除掉这个dom节点,这个时候如果用普通的map的话,dom节点虽然删除了,但是他仍然在map中被引用着,所以耗费内存,这时,使用weakMap就能实现,如下。

let wm = new WeakMap()

const btn = document.querySelector('#btn')

// 给btn节点添加一些关联数据等
m.set(btn, {disabled:true})
作者:Gill
链接:https://juejin.cn/post/6963668385699758116
来源:掘金

链接: https://www.fly63.com/article/detial/10306

es6 箭头函数的使用总结,带你深入理解js中的箭头函数

箭头函数是ES6中非常重要的性特性。它最显著的作用就是:更简短的函数,并且不绑定this,arguments等属性,它的this永远指向其上下文的 this。它最适合用于非方法函数,并且它们不能用作构造函数。

详解JavaScript模块化开发require.js

js模块化的开发并不是随心所欲的,为了便于他人的使用和交流,需要遵循一定的规范。目前,通行的js模块规范主要有两种:CommonJS和AMD

js解构赋值,关于es6中的解构赋值的用途总结

ES6中添加了一个新属性解构,允许你使用类似数组或对象字面量的语法将数组和对象的属性赋给各种变量。用途:交换变量的值、从函数返回多个值、函数参数的定义、提取JSON数据、函数参数的默认值...

ES6中let变量的特点,使用let声明总汇

ES6中let变量的特点:1.let声明变量存在块级作用域,2.let不能先使用再声明3.暂时性死区,在代码块内使用let命令声明变量之前,该变量都是不可用的,4.不允许重复声明

ES6的7个实用技巧

ES6的7个实用技巧包括:1交换元素,2 调试,3 单条语句,4 数组拼接,5 制作副本,6 命名参数,7 Async/Await结合数组解构

ES6 Decorator_js中的装饰器函数

ES6装饰器(Decorator)是一个函数,用来修改类的行为 在设计阶段可以对类和属性进行注释和修改。从本质上上讲,装饰器的最大作用是修改预定义好的逻辑,或者给各种结构添加一些元数据。

基于ES6的tinyJquery

Query作为曾经Web前端的必备利器,随着MVVM框架的兴起,如今已稍显没落。用ES6写了一个基于class简化版的jQuery,包含基础DOM操作,支持链式操作...

ES6 中的一些技巧,使你的代码更清晰,更简短,更易读!

ES6 中的一些技巧:模版字符串、块级作用域、Let、Const、块级作用域函数问题、扩展运算符、函数默认参数、解构、对象字面量和简明参数、动态属性名称、箭头函数、for … of 循环、数字字面量。

Rest/Spread 属性_探索 ES2018 和 ES2019

Rest/Spread 属性:rest操作符在对象解构中的使用。目前,该操作符仅适用于数组解构和参数定义。spread操作符在对象字面量中的使用。目前,这个操作符只能在数组字面量和函数以及方法调用中使用。

使用ES6让你的React代码提升到一个新档次

ES6使您的代码更具表现力和可读性。而且它与React完美配合!现在您已了解更多基础知识:现在是时候将你的ES6技能提升到一个新的水平!嵌套props解构、 传下所有props、props解构、作为参数的函数、列表解构

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!