diff options
author | Peter Bengtsson <mail@peterbe.com> | 2020-12-08 14:40:17 -0500 |
---|---|---|
committer | Peter Bengtsson <mail@peterbe.com> | 2020-12-08 14:40:17 -0500 |
commit | 33058f2b292b3a581333bdfb21b8f671898c5060 (patch) | |
tree | 51c3e392513ec574331b2d3f85c394445ea803c6 /files/zh-cn/web/javascript/reference/classes | |
parent | 8b66d724f7caf0157093fb09cfec8fbd0c6ad50a (diff) | |
download | translated-content-33058f2b292b3a581333bdfb21b8f671898c5060.tar.gz translated-content-33058f2b292b3a581333bdfb21b8f671898c5060.tar.bz2 translated-content-33058f2b292b3a581333bdfb21b8f671898c5060.zip |
initial commit
Diffstat (limited to 'files/zh-cn/web/javascript/reference/classes')
6 files changed, 1379 insertions, 0 deletions
diff --git a/files/zh-cn/web/javascript/reference/classes/class_elements/index.html b/files/zh-cn/web/javascript/reference/classes/class_elements/index.html new file mode 100644 index 0000000000..fb8c618a9b --- /dev/null +++ b/files/zh-cn/web/javascript/reference/classes/class_elements/index.html @@ -0,0 +1,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 world"</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 world" +</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 world" + +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(() => 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 world"</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 world✨" +</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> diff --git a/files/zh-cn/web/javascript/reference/classes/constructor/index.html b/files/zh-cn/web/javascript/reference/classes/constructor/index.html new file mode 100644 index 0000000000..334b80e7ad --- /dev/null +++ b/files/zh-cn/web/javascript/reference/classes/constructor/index.html @@ -0,0 +1,132 @@ +--- +title: 构造方法 +slug: Web/JavaScript/Reference/Classes/constructor +tags: + - Classes + - ECMAScript 2015 + - JavaScript +translation_of: Web/JavaScript/Reference/Classes/constructor +--- +<div>{{jsSidebar("Classes")}}</div> + +<p><strong> <code>constructor </code></strong>是一种用于创建和初始化<code><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/class">class</a></code>创建的对象的特殊方法。</p> + +<p>{{EmbedInteractiveExample("pages/js/classes-constructor.html")}}</p> + +<h2 id="语法">语法</h2> + +<pre class="syntaxbox">constructor([arguments]) { ... }</pre> + +<h2 id="描述">描述</h2> + +<p>在一个类中只能有一个名为 “constructor” 的特殊方法。 一个类中出现多次构造函数 (<code>constructor)</code>方法将会抛出一个 {{jsxref("SyntaxError")}} 错误。</p> + +<p>在一个构造方法中可以使用<code>super</code>关键字来调用一个父类的构造方法。</p> + +<p>如果没有显式指定构造方法,则会添加默认的 constructor 方法。</p> + +<p>如果不指定一个构造函数(constructor)方法, 则使用一个默认的构造函数(constructor)。</p> + +<h2 id="示例">示例</h2> + +<h3 id="使用constructor方法">使用<code>constructor</code>方法</h3> + +<p>以下代码片段来自 <a href="https://github.com/GoogleChrome/samples/blob/gh-pages/classes-es6/index.html">类的实例</a>(<a href="https://googlechrome.github.io/samples/classes-es6/index.html">在线 demo</a>)。</p> + +<pre class="brush: js">class Square extends Polygon { + constructor(length) { + // 在这里, 它调用了父类的构造函数, 并将 lengths 提供给 Polygon 的"width"和"height" + super(length, length); + // 注意: 在派生类中, 必须先调用 super() 才能使用 "this"。 + // 忽略这个,将会导致一个引用错误。 + this.name = 'Square'; + } + get area() { + return this.height * this.width; + } + set area(value) { + // 注意:不可使用 this.area = value + // 否则会导致循环call setter方法导致爆栈 + this._area = value; + } +} + +</pre> + +<h3 id="另一个例子">另一个例子</h3> + +<p>看看这个代码片段</p> + +<pre class="brush: js">class Polygon { + constructor() { + this.name = "Polygon"; + } +} + +class Square extends Polygon { + constructor() { + super(); + } +} + +class Rectangle {} + +Object.setPrototypeOf(Square.prototype, Rectangle.prototype); + +console.log(Object.getPrototypeOf(Square.prototype) === Polygon.prototype); //false +console.log(Object.getPrototypeOf(Square.prototype) === Rectangle.prototype); //true + +let newInstance = new Square(); +console.log(newInstance.name); //Polygon +</pre> + +<p>这里,<strong>Square</strong>类的原型被改变,但是在正在创建一个新的正方形实例时,仍然调用前一个基类<strong>Polygon</strong>的构造函数。</p> + +<h3 id="默认构造方法">默认构造方法</h3> + +<p>如前所述,如果不指定构造方法,则使用默认构造函数。对于基类,默认构造函数是:</p> + +<pre class="brush: js">constructor() {}</pre> + +<p>对于派生类,默认构造函数是:</p> + +<pre class="brush: js">constructor(...args) { + super(...args); +}</pre> + +<h2 id="标准">标准</h2> + +<table class="standard-table"> + <tbody> + <tr> + <th scope="col">Specification</th> + <th scope="col">Status</th> + <th scope="col">Comment</th> + </tr> + <tr> + <td>{{SpecName('ES2015', '#sec-static-semantics-constructormethod', 'Constructor Method')}}</td> + <td>{{Spec2('ES2015')}}</td> + <td>Initial definition.</td> + </tr> + <tr> + <td>{{SpecName('ESDraft', '#sec-static-semantics-constructormethod', 'Constructor Method')}}</td> + <td>{{Spec2('ESDraft')}}</td> + <td> </td> + </tr> + </tbody> +</table> + +<h2 id="浏览器兼容">浏览器兼容</h2> + +<div class="hidden">The compatibility table on this page is generated from structured data. If you'd like to contribute to the data, please check out <a href="https://github.com/mdn/browser-compat-data">https://github.com/mdn/browser-compat-data</a> and send us a pull request.</div> + +<p>{{Compat("javascript.classes.constructor")}}</p> + +<h2 id="参阅">参阅</h2> + +<ul> + <li><a href="/en-US/docs/Web/JavaScript/Reference/Operators/super">super()</a></li> + <li><a href="/en-US/docs/Web/JavaScript/Reference/Operators/class"><code>class</code> expression</a></li> + <li><a href="/en-US/docs/Web/JavaScript/Reference/Statements/class"><code>class</code> declaration</a></li> + <li><a href="/en-US/docs/Web/JavaScript/Reference/Classes">Classes</a></li> +</ul> diff --git a/files/zh-cn/web/javascript/reference/classes/extends/index.html b/files/zh-cn/web/javascript/reference/classes/extends/index.html new file mode 100644 index 0000000000..3166b658fc --- /dev/null +++ b/files/zh-cn/web/javascript/reference/classes/extends/index.html @@ -0,0 +1,106 @@ +--- +title: extends +slug: Web/JavaScript/Reference/Classes/extends +tags: + - Classes + - ECMAScript 2015 + - JavaScript +translation_of: Web/JavaScript/Reference/Classes/extends +--- +<div>{{jsSidebar("Classes")}}</div> + +<p><strong><code>extends</code></strong>关键字用于<a href="/zh-CN/docs/Web/JavaScript/Reference/Statements/class">类声明</a>或者<a href="/zh-CN/docs/Web/JavaScript/Reference/Operators/class">类表达式</a>中,以创建一个类,该类是另一个类的子类。</p> + +<h2 id="语法">语法</h2> + +<pre class="syntaxbox">class ChildClass extends ParentClass { ... }</pre> + +<h2 id="描述">描述</h2> + +<p><code>extends</code>关键字用来创建一个普通类或者内建对象的子类。</p> + +<p><font face="Open Sans, Arial, sans-serif">继承的</font><code>.prototype</code>必须是一个{{jsxref("Object")}} 或者 {{jsxref("null")}}。</p> + +<h2 id="示例">示例</h2> + +<h3 id="使用_extends">使用 <code>extends</code></h3> + +<p>第一个例子是根据名为 <code style="font-style: normal;">Polygon</code> 类创建一个名为<code>Square</code>的类。这个例子是从这个<a href="https://googlechrome.github.io/samples/classes-es6/index.html">在线演示</a>中提取出来的。</p> + +<pre class="brush: js">class Square extends Polygon { + constructor(length) { + // Here, it calls the parent class' constructor with lengths + // provided for the Polygon's width and height + super(length, length); + // Note: In derived classes, super() must be called before you + // can use 'this'. Leaving this out will cause a reference error. + this.name = 'Square'; + } + + get area() { + return this.height * this.width; + } +}</pre> + +<h3 id="使用_extends与内置对象">使用 <code>extends</code>与内置对象</h3> + +<p>这个示例继承了内置的{{jsxref("Date")}}对象。这个例子是从这个<a href="https://googlechrome.github.io/samples/classes-es6/index.html">在线演示</a>中提取出来的。</p> + +<pre class="brush: js">class myDate extends Date { + constructor() { + super(); + } + + getFormattedDate() { + var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; + return this.getDate() + "-" + months[this.getMonth()] + "-" + this.getFullYear(); + } +}</pre> + +<h3 id="扩展_null">扩展 <code>null</code></h3> + +<p>可以像扩展普通类一样扩展{{jsxref("null")}},但是新对象的原型将不会继承 {{jsxref("Object.prototype")}}。</p> + +<pre class="brush: js">class nullExtends extends null { + constructor() {} +} + +Object.getPrototypeOf(nullExtends); // Function.prototype +Object.getPrototypeOf(nullExtends.prototype) // null + +new nullExtends(); //ReferenceError: this is not defined</pre> + +<h2 id="标准">标准</h2> + +<table class="standard-table"> + <tbody> + <tr> + <th scope="col">Specification</th> + <th scope="col">Status</th> + <th scope="col">Comment</th> + </tr> + <tr> + <td>{{SpecName('ES2015', '#sec-class-definitions', 'extends')}}</td> + <td>{{Spec2('ES2015')}}</td> + <td>Initial definition.</td> + </tr> + <tr> + <td>{{SpecName('ESDraft', '#sec-class-definitions', 'extends')}}</td> + <td>{{Spec2('ESDraft')}}</td> + <td> </td> + </tr> + </tbody> +</table> + +<h2 id="浏览器兼容">浏览器兼容</h2> + +<div class="hidden">The compatibility table on this page is generated from structured data. If you'd like to contribute to the data, please check out <a href="https://github.com/mdn/browser-compat-data">https://github.com/mdn/browser-compat-data</a> and send us a pull request.</div> + +<p>{{Compat("javascript.classes.extends")}}</p> + +<h2 id="扩展阅读">扩展阅读</h2> + +<ul> + <li><a href="/zh-CN/docs/Web/JavaScript/Reference/Classes">类</a></li> + <li><a href="/zh-CN/docs/Web/JavaScript/Reference/Operators/super">super</a></li> +</ul> diff --git a/files/zh-cn/web/javascript/reference/classes/index.html b/files/zh-cn/web/javascript/reference/classes/index.html new file mode 100644 index 0000000000..f1f7cf7d87 --- /dev/null +++ b/files/zh-cn/web/javascript/reference/classes/index.html @@ -0,0 +1,450 @@ +--- +title: 类 +slug: Web/JavaScript/Reference/Classes +tags: + - ECMAScript 2015 + - Học + - JavaScript + - 中级 + - 构造函数 + - 类 + - 继承 + - 高阶类 +translation_of: Web/JavaScript/Reference/Classes +--- +<div></div> + +<div>{{JsSidebar("Classes")}}</div> + +<p>类是用于创建对象的模板。他们用代码封装数据以处理该数据。 JS中的类建立在原型上,但也具有某些语法和语义未与ES5类相似语义共享。</p> + +<h2 id="定义类">定义类</h2> + +<p>实际上,类是“特殊的<a href="/zh-CN/docs/Web/JavaScript/Reference/Functions">函数</a>”,就像你能够定义的<a href="/zh-CN/docs/Web/JavaScript/Reference/Operators/function">函数表达式</a>和<a href="/zh-CN/docs/Web/JavaScript/Reference/Statements/function">函数声明</a>一样,类语法有两个组成部分:<a href="/zh-CN/docs/Web/JavaScript/Reference/Operators/class">类表达式</a>和<a href="/zh-CN/docs/Web/JavaScript/Reference/Statements/class">类声明</a>。</p> + +<h3 id="类声明">类声明</h3> + +<p>定义类的一种方法是使用<strong>类声明</strong>。要声明一个类,你可以使用带有<code>class</code>关键字的类名(这里是“Rectangle”)。</p> + +<pre class="brush: js notranslate">class Rectangle { + constructor(height, width) { + this.height = height; + this.width = width; + } +}</pre> + +<h4 id="提升">提升</h4> + +<p><strong>函数声明</strong>和<strong>类声明</strong>之间的一个重要区别在于, 函数声明会{{Glossary("Hoisting", "提升")}},类声明不会。你首先需要声明你的类,然后再访问它,否则类似以下的代码将抛出{{jsxref("ReferenceError")}}:</p> + +<pre class="brush: js example-bad notranslate">let p = new Rectangle(); // ReferenceError + +class Rectangle {} +</pre> + +<h3 id="类表达式">类表达式</h3> + +<p><strong>类表达式</strong>是定义类的另一种方法。类表达式可以命名或不命名。命名类表达式的名称是该类体的局部名称。(不过,可以通过类的(而不是一个实例的) {{jsxref("Function.name", "name")}} 属性来检索它)。</p> + +<pre class="brush: js notranslate">// 未命名/匿名类 +let Rectangle = class { + constructor(height, width) { + this.height = height; + this.width = width; + } +}; +console.log(Rectangle.name); +// output: "Rectangle" + +// 命名类 +let Rectangle = class Rectangle2 { + constructor(height, width) { + this.height = height; + this.width = width; + } +}; +console.log(Rectangle.name); +// 输出: "Rectangle2"</pre> + +<div class="blockIndicator note"> +<p><strong>注意:</strong> 类<strong>表达式</strong>也同样受到<a href="https://wiki.developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Classes$edit#Class_declarations">类声明</a>部分中提到的类型提升的限制。</p> +</div> + +<h2 id="类体和方法定义">类体和方法定义</h2> + +<p>一个类的类体是一对花括号/大括号 <code>{}</code> 中的部分。这是你定义类成员的位置,如方法或构造函数。</p> + +<h3 id="严格模式">严格模式</h3> + +<p>类声明和类表达式的主体都执行在<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Strict_mode">严格模式</a>下。比如,构造函数,静态方法,原型方法,getter和setter都在严格模式下执行。</p> + +<h3 id="构造函数">构造函数</h3> + +<p><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/constructor">constructor</a>方法是一个特殊的方法,这种方法用于创建和初始化一个由<code>class</code>创建的对象。一个类只能拥有一个名为 “constructor”的特殊方法。如果类包含多个<code>constructor</code>的方法,则将抛出 一个{{jsxref("SyntaxError")}} 。</p> + +<p>一个构造函数可以使用 <code>super</code><strong> </strong>关键字来调用一个父类的构造函数。</p> + +<h3 id="原型方法">原型方法</h3> + +<p>参见<a href="/zh-CN/docs/Web/JavaScript/Reference/Functions/Method_definitions">方法定义</a>。</p> + +<pre class="brush: js notranslate">class Rectangle { + // constructor + constructor(height, width) { + this.height = height; + this.width = width; + } + // Getter + get area() { + return this.calcArea() + } + // Method + calcArea() { + return this.height * this.width; + } +} +const square = new Rectangle(10, 10); + +console.log(square.area); +// 100 +</pre> + +<h3 id="静态方法">静态方法</h3> + +<p><code><a href="/zh-CN/docs/Web/JavaScript/Reference/Classes/static">static</a></code> 关键字用来定义一个类的一个静态方法。调用静态方法不需要<a href="/zh-CN/docs/Web/JavaScript/Introduction_to_Object-Oriented_JavaScript#The_object_(class_instance)">实例化</a>该类,但不能通过一个类实例调用静态方法。静态方法通常用于为一个应用程序创建工具函数。</p> + +<pre class="brush: js notranslate">class Point { + constructor(x, y) { + this.x = x; + this.y = y; + } + + static distance(a, b) { + const dx = a.x - b.x; + const dy = a.y - b.y; + return Math.hypot(dx, dy); + } +} + +const p1 = new Point(5, 5); +const p2 = new Point(10,10); +p1.displayName; +// undefined +p1.distance; +// undefined + +console.log(Point.displayName); +// "Point" +console.log(Point.distance(p1, p2)); +// 7.0710678118654755 +</pre> + +<h3 id="用原型和静态方法绑定_this">用原型和静态方法绑定 this</h3> + +<p>当调用静态或原型方法时没有指定 <em>this </em>的值,那么方法内的 <em>this </em>值将被置为 <strong><code>undefined</code></strong>。即使你未设置 <font face="consolas, Liberation Mono, courier, monospace"><span style="background-color: rgba(220, 220, 220, 0.5);">"use strict"</span></font> ,因为 <code>class</code> 体内部的代码总是在严格模式下执行。</p> + +<pre class="brush: js notranslate">class Animal { + speak() { + return this; + } + static eat() { + return this; + } +} + +let obj = new Animal(); +obj.speak(); // Animal {} +let speak = obj.speak; +speak(); // undefined + +Animal.eat() // class Animal +let eat = Animal.eat; +eat(); // undefined</pre> + +<p>如果上述代码通过传统的基于函数的语法来实现,那么依据初始的 <em>this </em>值,在非严格模式下方法调用会发生自动装箱。若初始值是 <code>undefined</code>,<em>this</em> 值会被设为全局对象。</p> + +<p>严格模式下不会发生自动装箱,<em>this </em>值将保留传入状态。</p> + +<pre class="brush: js notranslate">function Animal() { } + +Animal.prototype.speak = function() { + return this; +} + +Animal.eat = function() { + return this; +} + +let obj = new Animal(); +let speak = obj.speak; +speak(); // global object + +let eat = Animal.eat; +eat(); // global object +</pre> + +<h3 id="实例属性">实例属性</h3> + +<p>实例的属性必须定义在类的方法里:</p> + +<pre class="notranslate"><code>class Rectangle { + constructor(height, width) { + this.height = height; + this.width = width; + } +}</code> +</pre> + +<p>静态的或原型的数据属性必须定义在类定义的外面。</p> + +<pre class="notranslate"><code>Rectangle.staticWidth = 20; +Rectangle.prototype.prototypeWidth = 25;</code> +</pre> + +<h3 id="字段声明">字段声明</h3> + +<div class="blockIndicator warning"> +<p>公共和私有字段声明是JavaScript标准委员会<a href="https://tc39.es/">TC39</a>提出的<a href="https://github.com/tc39/proposal-class-fields">实验性功能(第3阶段)</a>。浏览器中的支持是有限的,但是可以通过<a href="https://babeljs.io/">Babel</a>等系统构建后使用此功能。</p> +</div> + +<h4 id="公有字段声明">公有字段声明</h4> + +<p>使用JavaScript字段声明语法,上面的示例可以写成:</p> + +<pre class="notranslate"><code>class Rectangle { + height = 0; + width; + constructor(height, width) { + this.height = height; + this.width = width; + } +}</code> +</pre> + +<p>通过预先声明字段,类定义变得更加自我记录,并且字段始终存在。</p> + +<p>正如上面看到的,这个字段可以用也可以不用默认值来声明。</p> + +<h4 id="私有字段声明">私有字段声明</h4> + +<p>使用私有字段,可以按以下方式细化定义。</p> + +<pre class="notranslate"><code>class Rectangle { + #height = 0; + #width; + constructor(height, width) { + this.#height = height; + this.#width = width; + } +}</code> +</pre> + +<p>从类外部引用私有字段是错误的。它们只能在类里面中读取或写入。通过定义在类外部不可见的内容,可以确保类的用户不会依赖于内部,因为内部可能在不同版本之间发生变化。</p> + +<div class="blockIndicator note"> +<p>私有字段仅能在字段声明中预先定义。 </p> +</div> + +<p>私有字段不能通过在之后赋值来创建它们,这种方式只适用普通属性。</p> + +<p>更多信息,请看<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Class_fields">class fields</a>.</p> + +<h2 id="使用_extends_扩展子类">使用 <code>extends</code> 扩展子类</h2> + +<p><code><a href="/zh-CN/docs/Web/JavaScript/Reference/Classes/extends">extends</a></code> 关键字在 <em>类声明 </em>或 <em>类表达式 </em>中用于创建一个类作为另一个类的一个子类。</p> + +<pre class="brush: js notranslate">class Animal { + constructor(name) { + this.name = name; + } + + speak() { + console.log(`${this.name} makes a noise.`); + } +} + +class Dog extends Animal { + constructor(name) { + super(name); // 调用超类构造函数并传入name参数 + } + + speak() { + console.log(`${this.name} barks.`); + } +} + +var d = new Dog('Mitzie'); +d.speak();// 'Mitzie barks.' +</pre> + +<p>如果子类中定义了构造函数,那么它必须先调用 <code>super()</code> 才能使用 <code>this</code> 。</p> + +<p>也可以继承传统的基于函数的“类”:</p> + +<pre class="brush: js notranslate">function Animal (name) { + this.name = name; +} +Animal.prototype.speak = function () { + console.log(this.name + ' makes a noise.'); +} + +class Dog extends Animal { + speak() { + super.speak(); + console.log(this.name + ' barks.'); + } +} + +var d = new Dog('Mitzie'); +d.speak();//Mitzie makes a noise. Mitzie barks.</pre> + +<p>请注意,类不能继承常规对象(不可构造的)。如果要继承常规对象,可以改用{{jsxref("Object.setPrototypeOf()")}}:</p> + +<pre class="brush: js notranslate">var Animal = { + speak() { + console.log(this.name + ' makes a noise.'); + } +}; + +class Dog { + constructor(name) { + this.name = name; + } +} + +Object.setPrototypeOf(Dog.prototype, Animal);// 如果不这样做,在调用speak时会返回TypeError + +var d = new Dog('Mitzie'); +d.speak(); // Mitzie makes a noise.</pre> + +<h2 id="Species">Species</h2> + +<p>你可能希望在派生数组类 <em><code>MyArray</code> </em>中返回 {{jsxref("Array")}}对象。这种 species 方式允许你覆盖默认的构造函数。</p> + +<p>例如,当使用像{{jsxref("Array.map", "map()")}}返回默认构造函数的方法时,您希望这些方法返回一个父<code>Array</code>对象,而不是<code>MyArray</code>对象。{{jsxref("Symbol.species")}} 符号可以让你这样做:</p> + +<pre class="brush: js notranslate">class MyArray extends Array { + // Overwrite species to the parent Array constructor + static get [Symbol.species]() { return Array; } +} +var a = new MyArray(1,2,3); +var mapped = a.map(x => x * x); + +console.log(mapped instanceof MyArray); +// false +console.log(mapped instanceof Array); +// true +</pre> + +<h2 id="使用_super_调用超类">使用 <code>super</code> 调用超类</h2> + +<p><code><a href="/zh-CN/docs/Web/JavaScript/Reference/Operators/super">super</a></code> 关键字用于调用对象的父对象上的函数。</p> + +<pre class="brush: js notranslate">class Cat { + constructor(name) { + this.name = name; + } + + speak() { + console.log(this.name + ' makes a noise.'); + } +} + +class Lion extends Cat { + speak() { + super.speak(); + console.log(this.name + ' roars.'); + } +} +</pre> + +<h2 id="Mix-ins_混入">Mix-ins / 混入</h2> + +<p>抽象子类或者 mix-ins 是类的模板。 一个 ECMAScript 类只能有一个单超类,所以想要从工具类来多重继承的行为是不可能的。子类继承的只能是父类提供的功能性。因此,例如,从工具类的多重继承是不可能的。该功能必须由超类提供。</p> + +<p>一个以超类作为输入的函数和一个继承该超类的子类作为输出可以用于在ECMAScript中实现混合:</p> + +<pre class="brush: js notranslate">var calculatorMixin = Base => class extends Base { + calc() { } +}; + +var randomizerMixin = Base => class extends Base { + randomize() { } +};</pre> + +<p>使用 mix-ins 的类可以像下面这样写:</p> + +<pre class="brush: js notranslate">class Foo { } +class Bar extends calculatorMixin(randomizerMixin(Foo)) { }</pre> + +<h2 id="规范">规范</h2> + +<table> + <tbody> + <tr> + <th scope="col">Specification</th> + </tr> + <tr> + <td>{{SpecName('ESDraft', '#sec-class-definitions', 'Class definitions')}}</td> + </tr> + </tbody> +</table> + +<table class="standard-table"> + <tbody> + <tr> + <th scope="col">Specification</th> + <th scope="col">Status</th> + <th scope="col">Comment</th> + </tr> + <tr> + <td>{{SpecName('ES2015', '#sec-class-definitions', 'Class definitions')}}</td> + <td>{{Spec2('ES2015')}}</td> + <td>Initial definition.</td> + </tr> + <tr> + <td>{{SpecName('ES2016', '#sec-class-definitions', 'Class definitions')}}</td> + <td>{{Spec2('ES2016')}}</td> + <td></td> + </tr> + <tr> + <td>{{SpecName('ES2017', '#sec-class-definitions', 'Class definitions')}}</td> + <td>{{Spec2('ES2017')}}</td> + <td></td> + </tr> + <tr> + <td>{{SpecName('ESDraft', '#sec-class-definitions', 'Class definitions')}}</td> + <td>{{Spec2('ESDraft')}}</td> + <td></td> + </tr> + </tbody> +</table> + +<h2 id="浏览器兼容">浏览器兼容</h2> + + + +<p>{{Compat("javascript.classes")}}</p> + +<h2 id="重新运行一个类定义">重新运行一个类定义</h2> + +<p>无法重新定义类。尝试这样做会产生一个 <code>SyntaxError</code>.</p> + +<p>如果您正在使用Web浏览器(例如Firefox Web控制台, (<strong>Tools </strong>><strong> Web Developer </strong>><strong> Web Console</strong>)并且您两次“运行”具有相同名称的类的定义,您将收到一个 <code>SyntaxError: redeclaration of let <em>ClassName</em>;</code>. (请参阅中有关此问题的进一步讨论 {{Bug(1428672)}}.) 在Chrome开发者工具中执行类似的操作会给您一个以下信息: <code>Uncaught SyntaxError: Identifier '<em>ClassName</em>' has already been declared at <anonymous>:1:1</code>.</p> + +<h2 id="参见">参见</h2> + +<ul> + <li>{{jsxref("Functions", "Functions", "", "true")}}</li> + <li>{{jsxref("Statements/class", "class declaration", "", "true")}}</li> + <li>{{jsxref("Operators/class", "class expression", "", "true")}}</li> + <li>{{jsxref("Classes/Public_class_fields", "Public class fields", "", "true")}}</li> + <li>{{jsxref("Classes/Private_class_fields", "Private class fields", "", "true")}}</li> + <li>{{jsxref("Operators/super", "super")}}</li> + <li><a href="https://hacks.mozilla.org/2015/07/es6-in-depth-classes/">Blog post: "ES6 In Depth: Classes"</a></li> + <li><a href="https://github.com/tc39/proposal-class-fields">Fields and public/private class properties proposal (stage 3)</a></li> +</ul> + +<div id="gtx-trans" style="position: absolute; left: 160px; top: 11337px;"> +<div class="gtx-trans-icon"></div> +</div> diff --git a/files/zh-cn/web/javascript/reference/classes/private_class_fields/index.html b/files/zh-cn/web/javascript/reference/classes/private_class_fields/index.html new file mode 100644 index 0000000000..a320876884 --- /dev/null +++ b/files/zh-cn/web/javascript/reference/classes/private_class_fields/index.html @@ -0,0 +1,201 @@ +--- +title: 类私有域 +slug: Web/JavaScript/Reference/Classes/Private_class_fields +tags: + - JavaScript + - 类 + - 语言特性 +translation_of: Web/JavaScript/Reference/Classes/Private_class_fields +--- +<div>{{JsSidebar("Classes")}}</div> + +<p>类属性在默认情况下是公共的,可以被外部类检测或修改。在<a href="https://github.com/tc39/proposal-class-fields">ES2020 实验草案</a> 中,增加了定义私有类字段的能力,写法是使用一个#作为前缀。</p> + +<h2 id="语法">语法</h2> + +<pre class="brush: js notranslate">class ClassWithPrivateField { + #privateField +} + +class ClassWithPrivateMethod { + #privateMethod() { + return 'hello world' + } +} + +class ClassWithPrivateStaticField { + static #PRIVATE_STATIC_FIELD +} +</pre> + +<h2 id="例子"> 例子</h2> + +<h3 id="私有静态字段">私有静态字段</h3> + +<p>私有字段可以被类的构造方法(constructor)从内部声明。</p> + +<p>静态变量只能被静态方法调用的限制仍然成立。</p> + +<pre class="brush: js notranslate">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>私有静态字段有一个来源限制, 只有定义该私有静态字段的类能访问该字段。</p> + +<p>这可能会导致:当使用<strong><code>this</code></strong>时出现意想不到的行为。</p> + +<pre class="brush: js notranslate">class BaseClassWithPrivateStaticField { + static #PRIVATE_STATIC_FIELD + + static basePublicStaticMethod() { + this.#PRIVATE_STATIC_FIELD = 42 + return this.#PRIVATE_STATIC_FIELD + } +} + +class SubClass extends BaseClassWithPrivateStaticField { } + +assertThrows(() => SubClass.basePublicStaticMethod(), TypeError) +</pre> + +<h3 id="私有实例字段">私有实例字段</h3> + +<p>私有实例字段使用 <strong>#名称</strong>(发音为“哈希名称”)声明,这些名称以 <code>#</code>开头。 <code>#</code>是名称本身的一部分, 声明和访问时也需要加上。</p> + +<p>封装由语言强制执行。 从作用域之外引用#名称是语法错误。</p> + +<pre class="brush: js notranslate">class ClassWithPrivateField { + #privateField + + constructor() { + this.#privateField = 42 + this.#randomField = 666 // Syntax error + } +} + +const instance = new ClassWithPrivateField() +instance.#privateField === 42 // Syntax error +</pre> + +<h3 id="私有方法">私有方法</h3> + +<h4 id="私有静态方法">私有静态方法</h4> + +<p>像它们的公有等价方法一样,私有静态方法是在类本身而非类的实例上调用的。 像私有静态字段一样,只能从类声明内部访问它们。</p> + +<p>私有静态方法可能是生成器方法,异步方法和异步生成器方法。</p> + +<pre class="brush: js notranslate">class ClassWithPrivateStaticMethod { + static #privateStaticMethod() { + return 42 + } + + static publicStaticMethod1() { + return ClassWithPrivateStaticMethod.#privateStaticMethod(); + } + + static publicStaticMethod2() { + return this.#privateStaticMethod(); + } +} + +assert(ClassWithPrivateStaticField.publicStaticMethod1() === 42); +assert(ClassWithPrivateStaticField.publicStaticMethod2() === 42); +</pre> + +<p>使用<strong><code>this</code></strong>可能会导致意想不到的行为(因为<code>this</code>绑定规则适用)。</p> + +<pre class="brush: js notranslate">class Base { + static #privateStaticMethod() { + return 42; + } + static publicStaticMethod1() { + return Base.#privateStaticMethod(); + } + static publicStaticMethod2() { + return this.#privateStaticMethod(); + } +} + +class Derived extends Base {} + +console.log(Derived.publicStaticMethod1()); // 42 +console.log(Derived.publicStaticMethod2()); // TypeError +</pre> + +<h4 id="私有实例方法">私有实例方法</h4> + +<p>私有实例方法是类实例上可用的方法,它们的访问方式与私有实例字段相同。</p> + +<pre class="brush: js notranslate">class ClassWithPrivateMethod { + #privateMethod() { + return 'hello world' + } + + getPrivateMessage() { + return this.#privateMethod() + } +} + +const instance = new ClassWithPrivateMethod() +console.log(instance.getPrivateMessage()) +// expected output: "hello world"</pre> + +<p>私有实例方法可以是生成器方法,异步方法或异步生成器方法。 私有的getter和setter也是可能的:</p> + +<pre class="brush: js notranslate">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(); +// expected output: "✨hello world✨" +</pre> + +<h2 id="规范">规范</h2> + +<table class="standard-table"> + <thead> + <tr> + <th scope="col">Specification</th> + </tr> + </thead> + <tbody> + <tr> + <td>{{SpecName('Public and private instance fields', '#prod-FieldDefinition', 'FieldDefinition')}}</td> + </tr> + </tbody> +</table> + +<h2 id="浏览器兼容性">浏览器兼容性</h2> + +<div class="hidden">此页面上的兼容性表是根据结构化数据生成的。 如果您想贡献数据,请查看https://github.com/mdn/browser-compat-data并向我们发送请求请求。</div> + +<p>{{Compat("javascript.classes.private_class_fields")}}</p> + +<h2 id="相关链接">相关链接</h2> + +<ul> + <li><a href="/en-US/docs/Web/JavaScript/Reference/Classes/Public_class_fields">Public class fields</a></li> + <li><a href="https://rfrn.org/~shu/2018/05/02/the-semantics-of-all-js-class-elements.html">The Semantics of All JS Class Elements</a></li> +</ul> diff --git a/files/zh-cn/web/javascript/reference/classes/static/index.html b/files/zh-cn/web/javascript/reference/classes/static/index.html new file mode 100644 index 0000000000..f995fffdbc --- /dev/null +++ b/files/zh-cn/web/javascript/reference/classes/static/index.html @@ -0,0 +1,138 @@ +--- +title: static +slug: Web/JavaScript/Reference/Classes/static +tags: + - Classes + - ECMAScript 2015 + - ES6 + - JavaScript + - Static + - 静态方法 +translation_of: Web/JavaScript/Reference/Classes/static +--- +<div>{{jsSidebar("Classes")}}</div> + +<p>类(class)通过 <strong>static </strong>关键字定义静态方法。不能在类的实例上调用静态方法,而应该通过类本身调用。这些通常是实用程序方法,例如创建或克隆对象的功能。</p> + +<p> </p> + +<div>{{EmbedInteractiveExample("pages/js/classes-static.html")}}</div> + + + +<h2 id="语法">语法</h2> + +<pre class="syntaxbox">static methodName() { ... }</pre> + +<h2 id="描述">描述</h2> + +<p>静态方法调用直接在类上进行,不能在类的实例上调用。静态方法通常用于创建实用程序函数。</p> + +<h2 id="调用静态方法">调用静态方法</h2> + +<h3 id="从另一个静态方法">从另一个静态方法</h3> + +<p>静态方法调用同一个类中的其他静态方法,可使用 <code><a href="/en-US/docs/Web/JavaScript/Reference/Operators/this">this</a> </code>关键字。</p> + +<pre class="brush: js">class StaticMethodCall { + static staticMethod() { + return 'Static method has been called'; + } + static anotherStaticMethod() { + return this.staticMethod() + ' from another static method'; + } +} +StaticMethodCall.staticMethod(); +// 'Static method has been called' + +StaticMethodCall.anotherStaticMethod(); +// 'Static method has been called from another static method' +</pre> + +<h3 id="从类的构造函数和其他方法">从类的构造函数和其他方法</h3> + +<p>非静态方法中,不能直接使用 <code><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/this">this</a></code> 关键字来访问静态方法。而是要用类名来调用:<code>CLASSNAME.STATIC_METHOD_NAME()</code> ,或者用构造函数的属性来调用该方法: <code>this.constructor.STATIC_METHOD_NAME()</code>.</p> + +<pre class="brush: js">class StaticMethodCall { + constructor() { + console.log(StaticMethodCall.staticMethod()); + // 'static method has been called.' + console.log(this.constructor.staticMethod()); + // 'static method has been called.' + } + static staticMethod() { + return 'static method has been called.'; + } +}</pre> + +<h2 id="示例">示例</h2> + +<p>下面的例子说明了这几点:</p> + +<ol> + <li>静态方法如何在类上实现。</li> + <li>具有静态成员的类,可以被子类化 。</li> + <li>什么情况下静态方法可以调用,什么情况下不能调用。</li> +</ol> + +<p class="brush: js"> </p> + +<pre class="brush: js">class Tripple { + static tripple(n = 1) { + return n * 3; + } +} + + +class BiggerTripple extends Tripple { + static tripple(n) { + return super.tripple(n) * super.tripple(n); + } +} + + +console.log(Tripple.tripple());// 3 +console.log(Tripple.tripple(6));// 18 + +let tp = new Tripple(); + +console.log(BiggerTripple.tripple(3));// 81(不会受父类实例化的影响) +console.log(tp.tripple());// 'tp.tripple 不是一个函数'.</pre> + +<p class="brush: js"> </p> + +<h2 id="规范">规范</h2> + +<table class="standard-table"> + <tbody> + <tr> + <th scope="col">Specification</th> + <th scope="col">Status</th> + <th scope="col">Comment</th> + </tr> + <tr> + <td>{{SpecName('ES2015', '#sec-class-definitions', 'Class definitions')}}</td> + <td>{{Spec2('ES2015')}}</td> + <td>Initial definition.</td> + </tr> + <tr> + <td>{{SpecName('ESDraft', '#sec-class-definitions', 'Class definitions')}}</td> + <td>{{Spec2('ESDraft')}}</td> + <td> </td> + </tr> + </tbody> +</table> + +<h2 id="浏览器兼容">浏览器兼容</h2> + +<div class="hidden">The compatibility table on this page is generated from structured data. If you'd like to contribute to the data, please check out <a href="https://github.com/mdn/browser-compat-data">https://github.com/mdn/browser-compat-data</a> and send us a pull request.</div> + +<p>{{Compat("javascript.classes.static")}}</p> + +<h2 id="相关链接">相关链接</h2> + +<ul> + <li><a href="/zh-CN/docs/Web/JavaScript/Reference/Operators/class"><code>class</code>表达式</a></li> + <li><a href="/zh-CN/docs/Web/JavaScript/Reference/Statements/class"><code>class</code>声明</a></li> + <li><a href="/zh-CN/docs/Web/JavaScript/Reference/Classes">Classes</a></li> +</ul> |