JS系列: ES6新增的数据结构

本章涉及:
了解ES6新增的数据结构 Set、WeakSet、Map、WeakMap
了解JS中的强应用和弱应用
了解entries结构

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

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

根据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还有值的存在呢?

JS系列: ES6新增的数据结构_第1张图片

  • 创建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内存中所指向的地址。
JS系列: ES6新增的数据结构_第2张图片

但是呢,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] ... ]

你可能感兴趣的:(javascript)