本章波及:
理解ES6新增的数据结构 Set、WeakSet、Map、WeakMap
理解JS中的强利用和弱利用
理解entries构造

在ES6以前,JavaScript中就只有两种数据结构:数组对象。 精确的来说,对象 还不是一种数据结构,它的底层是hashTable实现的(不晓得对不对,听他人说的)。

ES6 中 新增了4中数据结构:SetWeakSetMapWeakMap。接下来,就顺次的认识一下。

依据MDN作为根据

Set

Set 对象容许你存储任何类型的惟一值,无论是原始值或者是对象援用。

Set对象是的汇合,你能够依照插入的程序迭代它的元素。 Set中的元素只会呈现一次,即 Set 中的元素是惟一的。

Set中没有索引值

set中值的唯一性

唯一性,就示意根本数据类型的值要相等,援用数据类型的地址要相等

然而呢?还是有几个特地要留神的几个值:

  • 针对0的解决
// +0 和 -0console.log(+0 === -0)   // trueconst set = new Set()set.add(+0)set.add(-0)console.log(set)  // Set(1) {0}
  • 针对NaN的解决
console.log(NaN === NaN)  // falseconst set = new Set()set.add(NaN)set.add(NaN)console.log(set)  // Set(1) {NaN}

在Set中也视为相等。

Set常见的办法和属性

constructor: 承受无参或者一个迭代器的参数,创立一个Set实例。

Set.prototype.size: 返回set中的元素个数。

Set.prototype.add(value): 在Set对象尾部增加一个元素。返回该Set对象。

Set.prototype.clear(): 移除Set对象内的所有元素,没有返回值。

Set.prototype.delete(value): 从set中删除和这个值相等的元素,返回boolean类型

Set.prototype.has(value): 判断set中是否存在某个元素,返回boolean类型

遍历

entries: 放弃跟Map返回的格局一样, key与value的值雷同

forEach: 遍历Set中的每个元素

keys: 与values()办法雷同,返回一个新的迭代器对象。

values: 与下面一样

代码演示

const arr = [4, 3, 2, 1, 5]// constructorconst set = new Set(arr)// sizeconsole.log(set.size) // 5// addconsole.log(set.add(6)) // Set(6) { 4, 3, 2, 1, 5, 6 }// deleteconsole.log(set.delete(1)) // true// hasconsole.log(set.has(1)) // false// clearconsole.log(set.clear()) // undefined

遍历的话,就不敲了。

WeakSet

WeakSet 对象容许你将弱放弃对象存储在一个汇合中,也是外部不能反复。

下面概念中提到了一个 弱放弃,怎么了解呢?

请看最上面的JavaScript中的强援用和弱援用。

WeakSet和Set的区别

  • WeakSet中只能寄存对象类型,不能寄存根本数据类型;
  • WeakSet对元素的援用是弱援用,如果没有其余援用对某个对象进行援用,那么GC能够对该对象进行回收;
  • 不能够被枚举。

WeakSet的常见办法

  • WeakSet.prototype.add(value):增加某个元素,返回WeakSet对象自身;
  • WeakSet.prototype.delete(value):从WeakSet中删除和这个值相等的元素,返回boolean类型;
  • WeakSet.prototype.has(value):判断WeakSet中是否存在某个元素,返回boolean类型;

不能被遍历。

应用场景,很少很少(代码量少,可能没遇到过)。

Map

Map 对象保留键值对,并且可能记住键的原始插入程序。任何值(对象或者原始值)都能够作为一个键或一个值。

在map以前,只有想着键值对的模式,必定想到对象。对,不可拥护。

那么Map对象之间有什么区别呢?

对象的key值只能是字符串,以及前面新增的symbol类型。然而,Map反对所有类型。

对象解决对象作为key的场景

let obj1 = { name: 'copyer' }let obj2 = {name: 'copyer'}const a = {  [obj1]: 'aaa',  [obj2]: 'bbb'}console.log(a)  // { '[object Object]': 'bbb' }

看进去,对象会先被转化为字符串[object Object], 字符串作为key值。

Map中的属性和办法

constructor: 承受无参或者entries构造的参数,创立一个Set实例。(了解entries构造,看上面的了解entries构造)。

Map.prototype.size: 返回 Map 对象中的键值对数量。

Map.prototype.clear(): 移除 Map 对象中所有的键值对。

Map.prototype.delete(key):移除 Map 对象中指定的键值对,如果键值对存在,返回 true,否则返回 false

Map.prototype.get(key): 返回与 key 关联的值,若不存在关联的值,则返回 undefined

Map.prototype.has(key): 返回一个布尔值,用来表明 Map 对象中是否存在与 key 关联的值。

Map.prototype.set(key, value): 在 Map 对象中设置与指定的键 key 关联的值 value,并返回 Map 对象。

遍历

for...of..

keys

values

entries

forEach

代码演示

const map = new Map([["name", "copyer"]]);// sizeconsole.log(map.size); // 1// setmap.set(1, "我是数字");// getconsole.log(map.get(1)); // 我是数字// hasconsole.log(map.has(1)); // true// deleteconsole.log(map.delete(1)); // true// clear// console.log(map.clear());// forEachmap.forEach((value, key) => {  console.log(value, key) // copyer name})// for...of...for (const [key, value] of map) {  console.log(key, value) // name copyer}

Map对象的比拟

借鉴于MDN的比照

MapObject
意外的键Map 默认状况不蕴含任何键。只蕴含显式插入的键。一个 Object 有一个原型, 原型链上的键名有可能和你本人在对象上的设置的键名产生抵触。
键的类型一个 Map 的键能够是任意值,包含函数、对象或任意根本类型。一个 Object 的键必须是一个 String 或是 Symbol
键的程序Map 中的 key 是有序的。因而,当迭代的时候,一个 Map 对象以插入的程序返回键值。尽管 Object 的键目前是有序的,但并不总是这样,而且这个程序是简单的。因而,最好不要依赖属性的程序。
SizeMap 的键值对个数能够轻易地通过size属性获取。Object 的键值对个数只能手动计算.
迭代Map是可迭代的的,所以能够间接被迭代。Object 没有实现 可迭代协定,所以应用 JavaSctipt 的 for...of 表达式并不能间接迭代对象。
性能在频繁增删键值对的场景下体现更好。在频繁增加和删除键值对的场景下未作出优化。
序列化和解析没有元素的序列化和解析的反对。原生的由Object到JSON的序列化反对,应用 JSON.stringify()。原生的由 JSON 到Object的解析反对,应用 JSON.parse()

其实通过下面的比拟,map大部分是优先于对象的,然而在开发过程中,还是基本上都是应用对象(可能是开发的级别不够)

Map 和 数组的转化

const kvArray = [["key1", "value1"], ["key2", "value2"]];// 应用惯例的 Map 构造函数能够将一个二维键值对数组转换成一个 Map 对象const myMap = new Map(kvArray);myMap.get("key1"); // 返回值为 "value1"// 应用 Array.from 函数能够将一个 Map 对象转换成一个二维键值对数组console.log(Array.from(myMap)); // 输入和 kvArray 雷同的数组// 更简洁的办法来做如上同样的事件,应用开展运算符console.log([...myMap]);// 或者在键或者值的迭代器上应用 Array.from,进而失去只含有键或者值的数组console.log(Array.from(myMap.keys())); // 输入 ["key1", "key2"]

WeakMap

WeakMap 对象是一组键/值对的汇合,其中的键是弱援用的。其键必须是对象,而值能够是任意的。

WeakMap和Map的区别

  • WeakMap的key值只能是对象,不能其余类型;
  • WeakSet对元素的援用是弱援用,如果没有其余援用对某个对象进行援用,那么GC能够对该对象进行回收;
  • 不能够被枚举。

WeakMap罕用的办法

constructor: 承受无参或者entries构造的参数,创立一个Set实例。(了解entries构造,看上面的了解entries构造)。

WeakMap.prototype.delete(key):移除 WeakMap 对象中指定的键值对,如果键值对存在,返回 true,否则返回 false

WeakMap.prototype.get(key): 返回与 key 关联的值,若不存在关联的值,则返回 undefined

WeakMap.prototype.has(key): 返回一个布尔值,用来表明 WeakMap 对象中是否存在与 key 关联的值。

WeakMap.prototype.set(key, value): 在 WeakMap 对象中设置与指定的键 key 关联的值 value,并返回 Map 对象。

应用场景

在Vue3响应式拦挡中,是应用了WeakMap。

了解JavaScript中的强援用和弱援用

JavaScript中的对象,只有没有被援用,就会被垃圾回收机制回收。

强援用

先看代码

let obj = { name: "copyer", age: 23 };const newObj = obj;obj = null;console.log(obj); // nullconsole.log(newObj); // { name: 'copyer', age: 23 }

下面的代码应该很简略吧。简略一个对象obj,把obj的援用赋值给另外一个变量newObj, 而后清空obj,打印两者。那么内存的表现形式呢?为什么obj为null, newObj还有值的存在呢?

  • 创立Obj对象,在heap中生成一个内存地址 0x100, stack中的obj的值:0x100
  • 把援用赋值给newObj,那么它的值也是0x100
  • 当执行 obj = null, 只是断开了obj 与 heap内存的连贯,并没有断开 newObj的连贯,所以obj为null,然而newObj是有值的。

当内存中开拓的地址,只有又被其余所指向,就不会被垃圾回收机制所回收。这就是强援用。(强势的一面)

弱援用

从字面上来说,就是出于弱势的一面,即便指向某一个援用,然而垃圾回收机制看不起你,你太弱了,强行把内存发出掉。

WeakSet 和 WeakMap就是 弱援用。

WeakMap实例:

let obj = { name: "copyer", age: 23 };let weakMap = new WeakMap([[obj, obj]])console.log(weakMap.get(obj)) // { name: "copyer", age: 23 }// 如果obj = nullconsole.log(weakMap.get(obj)) // undefined

定义一个obj对象,在内存中会开拓一块地址,在WeakMap的传参中,是援用了obj内存中所指向的地址。

然而呢,obj = null 时, 断开了连贯,按理说,还有其余的中央在援用,是不会被回收的。然而如果是弱援用,那么内存中的地址一样的会被回收,那么WeakMap参数援用 就没有指向了,所以是undefined

总结

强援用:只有有援用指向,就不会被回收。

弱援用:即便有援用指向,也是会被回收的。(WeakSet 和 WeakMap是JS弱援用的体现)

了解entries构造

其实何为entries构造, 调用一下 Object.entries()办法就晓得了。

const obj = {  name: 'copyer',  age: 23,  height: 180}console.log(Object.entries(obj)) // [ ['name', 'copyer'], ['age', 23], ['height', 180] ]

Object.entries()办法返回一个给定对象本身可枚举属性的键值对数组。(像一个二维数组)

其格局:

[ [key, value], [key, value] ... ]