本章波及:
理解 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 和 -0
console.log(+0 === -0) // true
const set = new Set()
set.add(+0)
set.add(-0)
console.log(set) // Set(1) {0}
- 针对
NaN
的解决
console.log(NaN === NaN) // false
const 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]
// constructor
const set = new Set(arr)
// size
console.log(set.size) // 5
// add
console.log(set.add(6)) // Set(6) {4, 3, 2, 1, 5, 6}
// delete
console.log(set.delete(1)) // true
// has
console.log(set.has(1)) // false
// clear
console.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"]]);
// size
console.log(map.size); // 1
// set
map.set(1, "我是数字");
// get
console.log(map.get(1)); // 我是数字
// has
console.log(map.has(1)); // true
// delete
console.log(map.delete(1)); // true
// clear
// console.log(map.clear());
// forEach
map.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); // null
console.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 = null
console.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] ... ]