-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
wb_zhouling03
committed
Nov 30, 2023
1 parent
82c33b4
commit bd935ad
Showing
3 changed files
with
360 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,352 @@ | ||
--- | ||
title: ES6 | ||
prev: | ||
text: 目录 | ||
link: ./index.md | ||
|
||
next: | ||
text: 目录 | ||
link: ./index.md | ||
--- | ||
|
||
## Set 和 Map 数据结构 | ||
|
||
### Set | ||
|
||
`new Set()` | ||
|
||
Set 类似于数组,但是成员的值都是唯一的,没有重复的值。 | ||
|
||
属性:size | ||
|
||
操作方法:add、delete、has、clear | ||
|
||
遍历方法:keys、values、entries、forEach | ||
|
||
#### Set 的应用场景 | ||
|
||
1. 数组去重 | ||
|
||
```js | ||
const arr = [1, 2, 3, 4, 5, 1, 2, 3, 4, 5]; | ||
console.log("通过扩展运算符将 Set 转为数组:", [...new Set(arr)]); | ||
console.log("通过 Array.from 将 Set 转为数组:", Array.from(new Set(arr))); | ||
``` | ||
|
||
2. 字符串去重 | ||
|
||
```js | ||
console.log([...new Set("ababbc")].join("")); | ||
``` | ||
|
||
3. 实现并集、交集、差集 | ||
|
||
```js | ||
const a = new Set([1, 2, 3]); | ||
const b = new Set([4, 3, 2]); | ||
|
||
// 并集 | ||
const union = new Set([...a, ...b]); | ||
|
||
// 交集 | ||
const intersect = new Set([...a].filter((x) => b.has(x))); | ||
|
||
// 差集 | ||
const difference = new Set([...a].filter((x) => !b.has(x))); | ||
``` | ||
|
||
### WeakSet | ||
|
||
`new WeakSet()` | ||
|
||
WeakSet 与 Set 类似,也是不重复的值的集合。但是,它与 Set 有两个区别: | ||
|
||
1. WeakSet 的成员只能是对象(除 null),而不能是其他类型的值 | ||
2. WeakSet 中的对象都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用(故 WeakSet 适合临时存放一组对象,以及存放跟对象绑定的信息) | ||
3. WeakSet 不可遍历,即不具有 size 属性,和 clear 方法,以及 Set 的遍历方法 | ||
|
||
操作方法:add、delete、has | ||
|
||
#### WeakSet 的应用场景 | ||
|
||
1. 储存 DOM 节点,不用担心节点从文档移除时,引发内存泄漏 | ||
|
||
2. 限制原型上的方法只能通过实例调用 | ||
|
||
```js | ||
const foos = new WeakSet(); | ||
|
||
class Foo { | ||
constructor() { | ||
foos.add(this); | ||
} | ||
|
||
method() { | ||
if (!foos.has(this)) { | ||
throw new TypeError("Foo.prototype.method 只能在 Foo 的实例上调用!"); | ||
} | ||
} | ||
} | ||
``` | ||
|
||
### Map | ||
|
||
`new Map()` | ||
|
||
ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键 | ||
|
||
属性:size | ||
|
||
操作方法:set、get、has、delete、clear | ||
|
||
遍历方法:keys、values、entries(默认迭代接口)、forEach | ||
|
||
#### Map 与其他数据结构的互相转换 | ||
|
||
1. Map 与数组 | ||
|
||
```js | ||
// 数组转 Map | ||
const map = new Map([ | ||
["name", "张三"], | ||
["title", "Author"] | ||
]); | ||
|
||
// Map 转数组 | ||
console.log([...map]); | ||
``` | ||
|
||
2. Map 与对象 | ||
|
||
如果 Map 有非字符串的键名,则这个键名会被转为字符串再作为对象的键名 | ||
|
||
```js | ||
const strMapToObj = (strMap) => { | ||
const obj = Object.create(null); | ||
for (let [k, v] of strMap) { | ||
obj[k] = v; | ||
} | ||
return obj; | ||
}; | ||
``` | ||
|
||
对象转 Map | ||
|
||
```js | ||
// 使用 Object.entries() 方法 | ||
const obj = { name: "张三", title: "Author" }; | ||
const map = new Map(Object.entries(obj)); | ||
|
||
// 自己转换 | ||
const objToStrMap = (obj) => { | ||
const strMap = new Map(); | ||
for (let k of Object.keys(obj)) { | ||
strMap.set(k, obj[k]); | ||
} | ||
return strMap; | ||
}; | ||
``` | ||
|
||
3. Map 与 JSON | ||
|
||
Map 转 JSON | ||
|
||
```js | ||
// Map 的键名都是字符串时,可以选择转为对象 JSON | ||
const strMapToJson = (strMap) => { | ||
return JSON.stringify(strMapToObj(strMap)); | ||
}; | ||
|
||
// Map 的键名有非字符串时,可以选择转为数组 JSON | ||
const mapToArrayJson = (map) => { | ||
return JSON.stringify([...map]); | ||
}; | ||
``` | ||
|
||
JSON 转 Map | ||
|
||
```js | ||
// JSON 转为 Map,正常情况下,所有键名都是字符串 | ||
const jsonToStrMap = (jsonStr) => { | ||
return objToStrMap(JSON.parse(jsonStr)); | ||
}; | ||
|
||
// 有一种特殊情况,整个 JSON 就是一个数组,且每个数组成员本身,又是一个有两个成员的数组 | ||
const jsonToMap = (jsonStr) => { | ||
return new Map(JSON.parse(jsonStr)); | ||
}; | ||
``` | ||
|
||
### WeakMap | ||
|
||
`new WeakMap()` | ||
|
||
WeakMap 与 Map 的区别有两点: | ||
|
||
1. WeakMap 只接受对象作为键名(null 除外),不接受其他类型的值作为键名 | ||
2. WeakMap 的键名所指向的对象,不计入垃圾回收机制。WeakMap 弱引用的只是键名,而不是键值。键值依然是正常引用 | ||
|
||
操作方法:set、get、has、delete | ||
|
||
#### WeakMap 的应用场景 | ||
|
||
1. DOM 节点作为键名 | ||
|
||
```js | ||
let myWeakmap = new WeakMap(); | ||
|
||
myWeakmap.set(document.getElementById("logo"), { timesClicked: 0 }); | ||
|
||
document.getElementById("logo").addEventListener( | ||
"click", | ||
function () { | ||
let logoData = myWeakmap.get(document.getElementById("logo")); | ||
logoData.timesClicked++; | ||
}, | ||
false | ||
); | ||
``` | ||
|
||
2. 部署私有属性 | ||
|
||
```js | ||
// 使用了 WeakMap 来存储私有属性 _counter 和 _action | ||
const _counter = new WeakMap(); | ||
const _action = new WeakMap(); | ||
|
||
class Countdown { | ||
/** | ||
* 构造函数 | ||
* @param {number} counter - 计数器初始值 | ||
* @param {function} action - 计数器为0时执行的回调函数 | ||
*/ | ||
constructor(counter, action) { | ||
_counter.set(this, counter); // 使用 WeakMap 存储私有属性 _counter | ||
_action.set(this, action); // 使用 WeakMap 存储私有属性 _action | ||
} | ||
|
||
/** | ||
* 计数器减1 | ||
*/ | ||
dec() { | ||
let counter = _counter.get(this); // 获取私有属性 _counter 的值 | ||
if (counter < 1) return; | ||
counter--; | ||
_counter.set(this, counter); // 更新私有属性 _counter 的值 | ||
if (counter === 0) { | ||
_action.get(this)(); // 执行私有属性 _action 中存储的回调函数 | ||
} | ||
} | ||
} | ||
``` | ||
|
||
### WeakRef | ||
|
||
WeakRef 对象,用于直接创建对象的弱引用 | ||
|
||
> 标准规定,一旦使用 WeakRef()创建了原始对象的弱引用,那么在本轮事件循环(event loop),原始对象肯定不会被清除,只会在后面的事件循环才会被清除 | ||
```js | ||
const target = { name: "张三" }; | ||
const ref = new WeakRef(target); | ||
``` | ||
|
||
WeakRef 实例对象有一个 deref()方法,如果原始对象存在,该方法返回原始对象;如果原始对象已经被垃圾回收机制清除,该方法返回 undefined | ||
|
||
#### WeakRef 的应用场景 | ||
|
||
1. 缓存,未被清除时可以从缓存取值,一旦清除缓存就自动失效 | ||
|
||
```js | ||
const makeWeakCached(f) { | ||
const cache = new Map(); | ||
return key => { | ||
const ref = cache.get(key); | ||
if (ref) { | ||
const cached = ref.deref(); | ||
if (cached !== undefined) return cached; | ||
} | ||
|
||
const fresh = f(key); | ||
cache.set(key, new WeakRef(fresh)); | ||
return fresh; | ||
} | ||
} | ||
``` | ||
|
||
### FinalizationRegistry | ||
|
||
清理器注册表功能 FinalizationRegistry,用来指定目标对象被垃圾回收机制清除以后,所要执行的回调函数 | ||
|
||
```js | ||
// 1. 新建注册表实例 | ||
const registry = new FinalizationRegistry((heldValue) => { | ||
// 回调函数的参数heldValue可以是任意类型的值,字符串、数值、布尔值、对象,甚至可以是undefined | ||
console.log("目标对象被清除了"); | ||
}); | ||
|
||
const target = {}; | ||
// 2. 注册表实例的register()方法,用来注册所要观察的目标对象 | ||
registry.register(target, "some value", target); | ||
|
||
// 3. 如果还想取消已经注册的回调函数,则要向register()传入第三个参数,作为标记值。这个标记值必须是对象,一般都用原始对象 | ||
registry.unregister(target); | ||
``` | ||
|
||
> 注册表不对目标对象构成强引用,属于弱引用。因为强引用的话,原始对象就不会被垃圾回收机制清除,这就失去使用注册表的意义了 | ||
#### FinalizationRegistry 的应用场景 | ||
|
||
1. 对上述的缓存函数进行增强 | ||
|
||
```js | ||
function makeWeakCached(f) { | ||
const cache = new Map(); | ||
const cleanup = new FinalizationRegistry((key) => { | ||
const ref = cache.get(key); | ||
if (ref && !ref.deref()) cache.delete(key); | ||
}); | ||
|
||
return (key) => { | ||
const ref = cache.get(key); | ||
if (ref) { | ||
const cached = ref.deref(); | ||
if (cached !== undefined) return cached; | ||
} | ||
|
||
const fresh = f(key); | ||
cache.set(key, new WeakRef(fresh)); | ||
cleanup.register(fresh, key); | ||
return fresh; | ||
}; | ||
} | ||
``` | ||
|
||
2. 如果由于某种原因,Thingy 类的实例对象没有调用 release()方法,就被垃圾回收机制清除了,那么清理器就会调用回调函数#cleanup(),输出一条错误信息 | ||
|
||
```js | ||
class Thingy { | ||
#file; | ||
#cleanup = (file) => { | ||
console.error( | ||
`The \`release\` method was never called for the \`Thingy\` for the file "${file.name}"` | ||
); | ||
}; | ||
#registry = new FinalizationRegistry(this.#cleanup); | ||
|
||
constructor(filename) { | ||
this.#file = File.open(filename); | ||
this.#registry.register(this, this.#file, this.#file); | ||
} | ||
|
||
release() { | ||
if (this.#file) { | ||
this.#registry.unregister(this.#file); | ||
File.close(this.#file); | ||
this.#file = null; | ||
} | ||
} | ||
} | ||
``` | ||
|
||
> 由于无法知道清理器何时会执行,所以最好避免使用它。另外,如果浏览器窗口关闭或者进程意外退出,清理器则不会运行 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
--- | ||
sidebar: false | ||
--- | ||
|
||
## ES6 | ||
|
||
[第 1 章 Set 和 Map 数据结构](./01.md) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,7 @@ | |
title: 红宝书 | ||
prev: | ||
text: 目录 | ||
link: ./README.md | ||
link: ./index.md | ||
|
||
next: | ||
text: HTML 中的 JavaScript | ||
|