Skip to content

Commit fab11f3

Browse files
committed
210
1 parent 8af572d commit fab11f3

File tree

2 files changed

+144
-1
lines changed

2 files changed

+144
-1
lines changed

readme.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
前端界的好文精读,每周更新!
88

9-
最新精读:<a href="./前沿技术/209.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%8D%95%E8%8E%B7%E6%89%80%E6%9C%89%E5%BC%82%E6%AD%A5%20error%E3%80%8B.md">209.精读《捕获所有异步 error》</a>
9+
最新精读:<a href="./前沿技术/210.%E7%B2%BE%E8%AF%BB%E3%80%8Aclass%20static%20block%E3%80%8B.md">210.精读《class static block》</a>
1010

1111
素材来源:[周刊参考池](https://github.com/ascoders/weekly/issues/2)
1212

@@ -167,6 +167,7 @@
167167
- <a href="./前沿技术/207.%E7%B2%BE%E8%AF%BB%E3%80%8ATypescript%20infer%20%E5%85%B3%E9%94%AE%E5%AD%97%E3%80%8B.md">207.精读《Typescript infer 关键字》</a>
168168
- <a href="./前沿技术/208.%E7%B2%BE%E8%AF%BB%E3%80%8ATypescript%204.4%E3%80%8B.md">208.精读《Typescript 4.4》</a>
169169
- <a href="./前沿技术/209.%E7%B2%BE%E8%AF%BB%E3%80%8A%E6%8D%95%E8%8E%B7%E6%89%80%E6%9C%89%E5%BC%82%E6%AD%A5%20error%E3%80%8B.md">209.精读《捕获所有异步 error》</a>
170+
- <a href="./前沿技术/210.%E7%B2%BE%E8%AF%BB%E3%80%8Aclass%20static%20block%E3%80%8B.md">210.精读《class static block》</a>
170171

171172
### 设计模式
172173

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
[class-static-block](https://github.com/tc39/proposal-class-static-block) 提案于 [2021.9.1](https://github.com/tc39/proposal-class-static-block/commit/c0cabee0aa2d036a8d902fea7bc1d179e3de2477) 进入 stage4,是一个基于 Class 增强的提案。
2+
3+
本周我们结合 [ES2022 feature: class static initialization blocks](https://2ality.com/2021/09/class-static-block.html) 这篇文章一起讨论一下这个特性。
4+
5+
## 概述
6+
7+
为什么我们需要 class static block 这个语法呢?其中一个原因是对 Class 静态变量的灵活赋值需求。以下面为例,我们想在 Class 内部对静态变量做批量初始化,就不得不写一个无用的 `_` 变量用来做初始化的逻辑:
8+
9+
```typescript
10+
class Translator {
11+
static translations = {
12+
yes: 'ja',
13+
no: 'nein',
14+
maybe: 'vielleicht',
15+
};
16+
static englishWords = [];
17+
static germanWords = [];
18+
static _ = initializeTranslator( // (A)
19+
this.translations, this.englishWords, this.germanWords);
20+
}
21+
function initializeTranslator(translations, englishWords, germanWords) {
22+
for (const [english, german] of Object.entries(translations)) {
23+
englishWords.push(english);
24+
germanWords.push(german);
25+
}
26+
}
27+
```
28+
29+
而且我们为什么把 `initializeTranslator` 写在外面呢?就因为在 Class 内部不能写代码块,但这造成一个严重的问题,是外部函数无法访问 Class 内部属性,所以需要做一堆枯燥的传值。
30+
31+
从这个例子看出,我们为了自定义一段静态变量初始化逻辑,需要做出两个妥协:
32+
33+
1. 在外部定义一个函数,并接受大量 Class 成员变量传参。
34+
2. 在 Class 内部定义一个无意义的变量 `_` 用来启动这个函数逻辑。
35+
36+
这实在太没有代码追求了,我们在 Class 内部做掉这些逻辑不就简洁了吗?这就是 class static block 特性:
37+
38+
```typescript
39+
class Translator {
40+
static translations = {
41+
yes: 'ja',
42+
no: 'nein',
43+
maybe: 'vielleicht',
44+
};
45+
static englishWords = [];
46+
static germanWords = [];
47+
static { // (A)
48+
for (const [english, german] of Object.entries(this.translations)) {
49+
this.englishWords.push(english);
50+
this.germanWords.push(german);
51+
}
52+
}
53+
}
54+
```
55+
56+
可以看到,`static` 关键字后面不跟变量,而是直接跟一个代码块,就是 class static block 语法的特征,在这个代码块内部,可以通过 `this` 访问 Class 所有成员变量,包括 `#` 私有变量。
57+
58+
原文对这个特性使用介绍就结束了,最后还提到一个细节,就是执行顺序。即所有 `static` 变量或区块都按顺序执行,父类优先执行:
59+
60+
```typescript
61+
class SuperClass {
62+
static superField1 = console.log('superField1');
63+
static {
64+
assert.equal(this, SuperClass);
65+
console.log('static block 1 SuperClass');
66+
}
67+
static superField2 = console.log('superField2');
68+
static {
69+
console.log('static block 2 SuperClass');
70+
}
71+
}
72+
73+
class SubClass extends SuperClass {
74+
static subField1 = console.log('subField1');
75+
static {
76+
assert.equal(this, SubClass);
77+
console.log('static block 1 SubClass');
78+
}
79+
static subField2 = console.log('subField2');
80+
static {
81+
console.log('static block 2 SubClass');
82+
}
83+
}
84+
85+
// Output:
86+
// 'superField1'
87+
// 'static block 1 SuperClass'
88+
// 'superField2'
89+
// 'static block 2 SuperClass'
90+
// 'subField1'
91+
// 'static block 1 SubClass'
92+
// 'subField2'
93+
// 'static block 2 SubClass'
94+
```
95+
96+
所以 Class 内允许有多个 class static block,父类和子类也可以有,不同执行顺序结果肯定不同,这个选择权交给了使用者,因为执行顺序和书写顺序一致。
97+
98+
## 精读
99+
100+
结合提案来看,class static block 还有一个动机,就是给了一个访问私有变量的机制:
101+
102+
```typescript
103+
let getX;
104+
105+
export class C {
106+
#x
107+
constructor(x) {
108+
this.#x = { data: x };
109+
}
110+
111+
static {
112+
// getX has privileged access to #x
113+
getX = (obj) => obj.#x;
114+
}
115+
}
116+
117+
export function readXData(obj) {
118+
return getX(obj).data;
119+
}
120+
```
121+
122+
理论上外部无论如何都无法访问 Class 私有变量,但上面例子的 `readXData` 就可以,而且不会运行时报错,原因就是其整个流程都是合法的,最重要的原因是,class static block 可以同时访问私有变量与全局变量,所以可以利用其做一个 “里应外合”。
123+
124+
不过我并不觉得这是一个好点子,反而像一个 "BUG",因为任何对规定的突破都会为可维护性埋下隐患,除非这个特性用在稳定的工具、框架层,用来做一些便利性工作,最终提升了应用编码的体验,这种用法是可以接受的。
125+
126+
最后要意识到,class static block 本质上并没有增加新功能,我们完全可以用普通静态变量代替,只是写起来很不自然,所以这个特性可以理解为对缺陷的补充,或者是语法完善。
127+
128+
## 总结
129+
130+
总的来说,class static block 在 Class 内创建了一个块状作用域,这个作用域内拥有访问 Class 内部私有变量的特权,且这个块状作用域仅在引擎调用时初始化执行一次,是一个比较方便的语法。
131+
132+
原文下方有一些反对声音,说这是对 JS 的复杂化,也有诸如 JS 越来越像 Java 的声音,不过我更赞同作者的观点,也就是 Js 中 Class 并不是全部,现在越来越多代码使用函数式语法,即便使用了 Class 的场景也会存在大量函数申明,所以 class static block 这个提案对开发者的感知实际上并不大。
133+
134+
> 讨论地址是:[精读《class static block》· Issue #351 · dt-fe/weekly](https://github.com/dt-fe/weekly/issues/351)
135+
136+
**如果你想参与讨论,请 [点击这里](https://github.com/dt-fe/weekly),每周都有新的主题,周末或周一发布。前端精读 - 帮你筛选靠谱的内容。**
137+
138+
> 关注 **前端精读微信公众号**
139+
140+
<img width=200 src="https://img.alicdn.com/tfs/TB165W0MCzqK1RjSZFLXXcn2XXa-258-258.jpg">
141+
142+
> 版权声明:自由转载-非商用-非衍生-保持署名([创意共享 3.0 许可证](https://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh)

0 commit comments

Comments
 (0)