本章波及:
理解ES6新增的数据结构 Set、WeakSet、Map、WeakMap
理解JS中的强利用和弱利用
理解entries构造
在ES6以前,JavaScript中就只有两种数据结构:数组
和 对象
。 精确的来说,对象
还不是一种数据结构,它的底层是hashTable实现的(不晓得对不对,听他人说的)。
ES6 中 新增了4
中数据结构:Set
、WeakSet
、Map
和 WeakMap
。接下来,就顺次的认识一下。
依据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的比照
Map | Object | |
---|---|---|
意外的键 | Map 默认状况不蕴含任何键。只蕴含显式插入的键。 | 一个 Object 有一个原型, 原型链上的键名有可能和你本人在对象上的设置的键名产生抵触。 |
键的类型 | 一个 Map 的键能够是任意值,包含函数、对象或任意根本类型。 | 一个 Object 的键必须是一个 String 或是 Symbol 。 |
键的程序 | Map 中的 key 是有序的。因而,当迭代的时候,一个 Map 对象以插入的程序返回键值。 | 尽管 Object 的键目前是有序的,但并不总是这样,而且这个程序是简单的。因而,最好不要依赖属性的程序。 |
Size | Map 的键值对个数能够轻易地通过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] ... ]