aboutsummaryrefslogtreecommitdiff
path: root/files/zh-cn/web/javascript/reference/classes/public_class_fields/index.html
blob: fb8c618a9bf1d0506c72a06833d405ddf884d333 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
---
title: 类元素
slug: Web/JavaScript/Reference/Classes/Class_elements
tags:
  - Class
  - JavaScript
  - 类
translation_of: Web/JavaScript/Reference/Classes/Public_class_fields
---
<div>{{JsSidebar("Classes")}}</div>

<div class="note">公有(public)和私有(private)字段声明是一个JavaScript标准委员会<a href="https://tc39.github.io/beta/">TC39</a>提议的<a href="https://github.com/tc39/proposal-class-fields">试验性功能 (第3阶段)</a>。这项功能在浏览器中的支持还受限,但你可以通过<a href="https://babeljs.io/">Babel</a>等构建系统来使用它。参见下面的<a href="#Browser_compatibility">兼容性信息</a></div>

<h2 id="公有字段">公有字段</h2>

<p>静态公有字段和实例公有字段都是可编辑的,可遍历的,可配置的。它们本身不同于私有对应值(private counterparts)的是,它们参与原型的继承。</p>

<h3 id="静态公有字段">静态公有字段</h3>

<p>静态公有字段在你想要创建一个只在每个类里面只存在一份,而不会存在于你创建的每个类的实例中的属性时可以用到。你可以用它存放缓存数据、固定结构数据或者其他你不想在所有实例都复制一份的数据。</p>

<p>静态公有字段是使用关键字 <code>static</code> 声明的。我们在声明一个类的时候,使用<a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty">Object.defineProperty</a>方法将静态公有字段添加到类的构造函数中。在类被声明之后,可以从类的构造函数访问静态公有字段。</p>

<pre class="brush: js">class ClassWithStaticField {
  static staticField = 'static field';
}

console.log(ClassWithStaticField.staticField);
// 预期输出值: "static field"​
</pre>

<p>没有设定初始化程序的字段将默认被初始化为<code>undefined</code></p>

<pre class="brush: js">class ClassWithStaticField {
  static staticField;
}

console.assert(ClassWithStaticField.hasOwnProperty('staticField'));
console.log(ClassWithStaticField.staticField);
// 预期输出值: "undefined"</pre>

<p>静态公有字段不会在子类里重复初始化,但我们可以通过原型链访问它们。</p>

<pre class="brush: js">class ClassWithStaticField {
  static baseStaticField = 'base field';
}

class SubClassWithStaticField extends ClassWithStaticField {
  static subStaticField = 'sub class field';
}

console.log(SubClassWithStaticField.subStaticField);
// 预期输出值: "sub class field"

console.log(SubClassWithStaticField.baseStaticField);
// 预期输出值: "base field"</pre>

<p>当初始化字段时,<code>this</code>指向的是类的构造函数。你可以通过名字引用构造函数,并使用<code>super</code>获取到存在的超类构造函数。</p>

<pre class="brush: js">class ClassWithStaticField {
  static baseStaticField = 'base static field';
  static anotherBaseStaticField = this.baseStaticField;

  static baseStaticMethod() { return 'base static method output'; }
}

class SubClassWithStaticField extends ClassWithStaticField {
  static subStaticField = super.baseStaticMethod();
}

console.log(ClassWithStaticField.anotherBaseStaticField);
// 预期输出值: "base static field"

console.log(SubClassWithStaticField.subStaticField);
// 预期输出值: "base static method output"
</pre>

<h3 id="公有实例字段">公有实例字段</h3>

<p>公有实例字段存在于类的每一个实例中。通过声明一个公有字段,我们可以确保该字段一直存在,而类的定义则会更加像是自我描述。</p>

<p>公有实例字段可以在基类的构造过程中(构造函数主体运行前)使用<a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty">Object.defineProperty</a>添加,也可以在子类构造函数中的<code>super()</code>函数结束后添加。</p>

<pre class="brush: js">class ClassWithInstanceField {
  instanceField = 'instance field';
}

const instance = new ClassWithInstanceField();
console.log(instance.instanceField);
// 预期输出值: "instance field"</pre>

<p>没有设定初始化程序的字段将默认被初始化为<code>undefined</code></p>

<pre class="brush: js">class ClassWithInstanceField {
  instanceField;
}

const instance = new ClassWithInstanceField();
console.assert(instance.hasOwnProperty('instanceField'));
console.log(instance.instanceField);
// 预期输出值: "undefined"</pre>

<p>和属性(properties)一样,字段名可以由计算得出。</p>

<pre class="brush: js">const PREFIX = 'prefix';

class ClassWithComputedFieldName {
    [`${PREFIX}Field`] = 'prefixed field';
}

const instance = new ClassWithComputedFieldName();
console.log(instance.prefixField);
// 预期输出值: "prefixed field"</pre>

<p>当初始化字段时,<code>this</code>指向的是类正在构造中的实例。和公共实例方法相同的是:你可以在子类中使用<code>super</code>来访问超类的原型。</p>

<pre class="brush: js">class ClassWithInstanceField {
  baseInstanceField = 'base field';
  anotherBaseInstanceField = this.baseInstanceField;
  baseInstanceMethod() { return 'base method output'; }
}

class SubClassWithInstanceField extends ClassWithInstanceField {
  subInstanceField = super.baseInstanceMethod();
}

const base = new ClassWithInstanceField();
const sub = new SubClassWithInstanceField();

console.log(base.anotherBaseInstanceField);
// 预期输出值: "base field"

console.log(sub.subInstanceField);
// 预期输出值: "base method output"</pre>

<h2 id="公共方法">公共方法</h2>

<h3 id="静态公共方法">静态公共方法</h3>

<p>关键字<code><strong>static</strong></code>将为一个类定义一个静态方法。静态方法不会在实例中被调用,而只会被类本身调用。它们经常是工具函数,比如用来创建或者复制对象。</p>

<p>{{EmbedInteractiveExample("pages/js/classes-static.html")}}</p>

<div class="hidden">
<p>这些交互案例的资源储存在GitHub仓库中。如果你想参与这个项目,请复制链接<a href="https://github.com/mdn/interactive-examples">https://github.com/mdn/interactive-examples</a>并向我们发送拉取(pull)请求。</p>
</div>

<p>静态方法是在类的赋值阶段用<a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty">Object.defineProperty</a>方法添加到类中的。静态方法是可编辑的、不可遍历的和可配置的。</p>

<h3 id="公共实例方法">公共实例方法</h3>

<p>正如其名,公共实例方法是可以在类的实例中使用的。</p>

<pre class="brush: js">class ClassWithPublicInstanceMethod {
  publicMethod() {
    return 'hello world';
  }
}

const instance = new ClassWithPublicInstanceMethod();
console.log(instance.publicMethod());
// 预期输出值: "hello worl​d"</pre>

<p>公共实例方法是在类的赋值阶段用<a href="/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty">Object.defineProperty</a>方法添加到类中的。静态方法是可编辑的、不可遍历的和可配置的。</p>

<p>你可以使用生成器(generator)、异步和异步生成器方法。</p>

<pre class="brush: js">class ClassWithFancyMethods {
  *generatorMethod() { }
  async asyncMethod() { }
  async *asyncGeneratorMethod() { }
}</pre>

<p>在实例的方法中,<code>this</code>指向的是实例本身,你可以使用<code>super</code>访问到超类的原型,由此你可以调用超类的方法。</p>

<pre class="brush: js">class BaseClass {
  msg = 'hello world';
  basePublicMethod() {
    return this.msg;
  }
}

class SubClass extends BaseClass {
  subPublicMethod() {
    return super.basePublicMethod();
  }
}

const instance = new SubClass();
console.log(instance.subPublicMethod());
// 预期输出值: "hello worl​d"
</pre>

<p><code>getter</code><code>setter</code>是和类的属性绑定的特殊方法,分别会在其绑定的属性被取值、赋值时调用。使用<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get">get</a><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/set">set</a>句法定义实例的公共<code>getter</code><code>setter</code></p>

<pre class="brush: js">class ClassWithGetSet {
  #msg = 'hello world';
  get msg() {
    return this.#msg;
  }
  set msg(x) {
    this.#msg = `hello ${x}`;
  }
}

const instance = new ClassWithGetSet();
console.log(instance.msg);
// expected output: "hello worl​d"

instance.msg = 'cake';
console.log(instance.msg);
// 预期输出值: "hello cake"
</pre>

<h2 id="私有字段">私有字段</h2>

<h3 id="静态私有字段">静态私有字段</h3>

<p>静态私有字段可以在类声明本身内部的构造函数上被访问到。</p>

<p>静态变量只能被静态方法访问的限制依然存在。</p>

<pre class="brush: js">class ClassWithPrivateStaticField {
  static #PRIVATE_STATIC_FIELD;

  static publicStaticMethod() {
    ClassWithPrivateStaticField.#PRIVATE_STATIC_FIELD = 42;
    return ClassWithPrivateStaticField.#PRIVATE_STATIC_FIELD;
  }
}

assert(ClassWithPrivateStaticField.publicStaticMethod() === 42);</pre>

<p>静态私有字段是在类赋值的时候被添加到类构造函数中的。</p>

<p>静态私有字段有一个来源限制。只有定义静态私有字段的类可以访问该字段。这在使用<strong><code>this</code></strong>时,可能会导致不符合预期的行为。</p>

<pre class="brush: js">class BaseClassWithPrivateStaticField {
  static #PRIVATE_STATIC_FIELD;

  static basePublicStaticMethod() {
    this.#PRIVATE_STATIC_FIELD = 42;
    return this.#PRIVATE_STATIC_FIELD;
  }
}

class SubClass extends BaseClassWithPrivateStaticField { }

assertThrows(() =&gt; SubClass.basePublicStaticMethod(), TypeError);
</pre>

<h3 id="私有实例字段">私有实例字段</h3>

<p>私有实例字段是通过<strong># names</strong>句型(读作“哈希名称”)声明的,即为识别符加一个前缀“#”。“#”是名称的一部分,也用于访问和声明。</p>

<p>封装是语言强制实施的。引用不在作用域内的 # names 是语法错误。</p>

<pre class="brush: js">class ClassWithPrivateField {
  #privateField;

  constructor() {
    this.#privateField = 42;
    this.#randomField = 666; # Syntax error
  }
}

const instance = new ClassWithPrivateField();
instance.#privateField === 42; // Syntax error
</pre>

<h2 id="私有方法">私有方法</h2>

<h3 id="静态私有方法">静态私有方法</h3>

<p>和静态公共方法一样,静态私有方法也是在类里面而非实例中调用的。和静态私有字段一样,它们也只能在类的声明中访问。</p>

<p>你可以使用生成器(generator)、异步和异步生成器方法。</p>

<p>静态私有方法可以是生成器、异步或者异步生成器函数。</p>

<pre class="brush: js">class ClassWithPrivateStaticMethod {
    static #privateStaticMethod() {
        return 42;
    }

    static publicStaticMethod() {
        return ClassWithPrivateStaticMethod.#privateStaticMethod();
    }
}

assert(ClassWithPrivateStaticMethod.publicStaticMethod() === 42);
</pre>

<h3 id="私有实例方法">私有实例方法</h3>

<p>私有实例方法在类的实例中可用,它的访问方式的限制和私有实例字段相同。</p>

<pre class="brush: js">class ClassWithPrivateMethod {
  #privateMethod() {
    return 'hello world';
  }

  getPrivateMessage() {
      return #privateMethod();
  }
}

const instance = new ClassWithPrivateMethod();
console.log(instance.getPrivateMessage());
// 预期输出值: "hello worl​d"</pre>

<p>私有实例方法可以是生成器、异步或者异步生成器函数。私有<code>getter</code><code>setter</code>也是可能的:</p>

<pre class="brush: js">class ClassWithPrivateAccessor {
  #message;

  get #decoratedMessage() {
    return `✨${this.#message}✨`;
  }
  set #decoratedMessage(msg) {
    this.#message = msg;
  }

  constructor() {
    this.#decoratedMessage = 'hello world';
    console.log(this.#decoratedMessage);
  }
}

new ClassWithPrivateAccessor();
// 预期输出值: "✨hello worl​d✨"
</pre>

<h2 id="浏览器兼容性">浏览器兼容性</h2>

<h3 id="公共类字段">公共类字段</h3>

<div class="hidden">此页面上的兼容性表是根据结构化数据生成的。 如果您想为数据做出贡献,请查看<a href="https://github.com/mdn/browser-compat-data">https://github.com/mdn/browser-compat-data</a>并向我们发送拉取请求。</div>

<p>{{Compat("javascript.classes.public_class_fields")}}</p>

<h3 id="私有类字段">私有类字段</h3>

<div class="hidden">此页面上的兼容性表是根据结构化数据生成的。 如果您想为数据做出贡献,请查看<a href="https://github.com/mdn/browser-compat-data">https://github.com/mdn/browser-compat-data</a>并向我们发送拉取请求。</div>

<p>{{Compat("javascript.classes.private_class_fields")}}</p>

<h2 id="另请参考:">另请参考:</h2>

<ul>
 <li><a href="https://rfrn.org/~shu/2018/05/02/the-semantics-of-all-js-class-elements.html">所有JS类元素的语义</a></li>
</ul>