简体   繁体   English

如何在 ES6 类中创建“公共静态字段”?

[英]How do I make a “public static field” in an ES6 class?

I'm making a Javascript class and I'd like to have a public static field like in Java.我正在制作一个 Javascript 类,我想要一个像 Java 一样的公共静态字段。 This is the relevant code:这是相关代码:

export default class Agent {
    CIRCLE: 1,
    SQUARE: 2,
    ...

This is the error I get:这是我得到的错误:

line 2, col 11, Class properties must be methods. Expected '(' but instead saw ':'.

It looks like ES6 modules don't allow this.看起来 ES6 模块不允许这样做。 Is there a way to get the desired behavior or do I have to write a getter?有没有办法获得所需的行为,还是我必须编写一个吸气剂?

You make "public static field" using accessor and a "static" keyword:您使用访问器和“静态”关键字创建“公共静态字段”:

class Agent {
    static get CIRCLE() {
      return 1;
    }
    static get SQUARE() {
      return 2;
    }
}

Agent.CIRCLE; // 1

Looking at a spec, 14.5 — Class Definitions — you'd see something suspiciously relevant :)查看规范14.5 — 类定义 — 您会看到一些可疑的相关内容:)

ClassElement[Yield] :类元素[产量]:
MethodDefinition[?Yield]方法定义[?Yield]
static MethodDefinition[?Yield] ;静态MethodDefinition[?Yield] ;

So from there you can follow to 14.5.14 — Runtime Semantics: ClassDefinitionEvaluation — to double check if it really does what it looks like it does.所以从那里你可以遵循14.5.14 — 运行时语义:ClassDefinitionEvaluation — 仔细检查它是否真的像它看起来那样做。 Specifically, step 20:具体来说,步骤20:

  1. For each ClassElement m in order from methods对于每个 ClassElement m 按顺序从方法
    1. If IsStatic of m is false , then如果m 的 IsStatic 为 false ,则
      1. Let status be the result of performing PropertyDefinitionEvaluation for m with arguments proto and false.让 status 是对带有参数 proto 和 false 的 m 执行 PropertyDefinitionEvaluation 的结果。
    2. Else,别的,
      1. Let status be the result of performing PropertyDefinitionEvaluation for m with arguments F and false.令 status 是使用参数 F 和 false 对 m 执行 PropertyDefinitionEvaluation 的结果。
    3. If status is an abrupt completion, then如果状态是突然完成,则
      1. Set the running execution context's LexicalEnvironment to lex.将运行执行上下文的 LexicalEnvironment 设置为 lex。
      2. Return status.返回状态。

IsStatic is defined earlier in 14.5.9 IsStatic在早期定义14.5.9

ClassElement : static MethodDefinition ClassElement : 静态方法定义
Return true.返回真。

So PropertyMethodDefinition is called with "F" (constructor, function object) as an argument, which in its turn creates an accessor method on that object .因此, PropertyMethodDefinition以“F”(构造函数,函数对象)作为参数被调用,它反过来在该对象上创建一个访问器方法

This already works in at least IETP (tech preview), as well as 6to5 and Traceur compilers.这至少已经在 IETP(技术预览版)以及 6to5 和 Traceur 编译器中起作用。

Since ECMAScript 2022, you can do something like this, similar to traditional class-oriented languages like Java and C#:从 ECMAScript 2022 开始,你可以做这样的事情,类似于传统的面向类的语言,比如 Java 和 C#:

class MyClass {
    static myStaticProp = 42;
    myProp = 42;
    myProp2 = this.myProp;
    myBoundFunc = () => { console.log(this.myProp); };

    constructor() {
        console.log(MyClass.myStaticProp); // Prints '42'
        console.log(this.myProp); // Prints '42'
        this.myBoundFunc(); // Prints '42'
    }
}

The above is equivalent to:以上相当于:

class MyClass {
    constructor() {
        this.myProp = 42;
        this.myProp2 = this.myProp;
        this.myBoundFunc = () => { console.log(this.myProp); };

        console.log(MyClass.myStaticProp); // Prints '42'
        console.log(this.myProp); // Prints '42'
        this.myBoundFunc(); // Prints '42'
    }
}
MyClass.myStaticProp = 42;

The features were added in the "Static Class Features" and "Class Fields" proposals by Daniel Ehrenberg et al.这些特征是 Daniel Ehrenberg 等人在“静态类特征”“类字段”提案中添加的。 Google Chrome (and new Edge) started supporting both proposals in version 72 , equivalent to Node.js 12+ .谷歌浏览器(和新的 Edge)在72 版本中开始支持这两个提案,相当于Node.js 12+ Firefox supports public instance fields since version 69 and static instance fields since version 75 . Firefox 自版本 69起支持公共实例字段,自版本 75起支持静态实例字段。 Safari supports both since version 14.1 . Safari 从14.1 版开始支持两者。 See more info at caniuse.com .caniuse.com 上查看更多信息。

For older browsers that don't yet support these features, you can use Babel to transpile class fields.对于尚不支持这些功能的旧浏览器,您可以使用Babel转译类字段。 This requires @babel/plugin-proposal-class-properties to be enabled (enabled by default in @babel/plugin-env starting from v7.14.0 ).这需要启用@babel/plugin-proposal-class-properties (从v7.14.0开始,在 @babel/plugin-env 中默认启用)。


Compared to @kangax's solution of declaring a getter, this solution can also be more performant, since here the property is accessed directly instead of through calling a function.与@kangax 声明getter 的解决方案相比,该解决方案的性能也更高,因为这里的属性是直接访问的,而不是通过调用函数来访问的。


Edit : A unified class fields proposal is now at stage 3.编辑:统一的类字段提案现在处于第 3 阶段。

Edit (February 2020) : The static class features have been split out into a different proposal.编辑(2020 年 2 月) :静态类功能已拆分为不同的提案。 Thanks @GOTO0!谢谢@GOTO0!

Edit (March 2021) : With the exception of Safari, all major browsers released after April 2020 now support this feature!编辑(2021 年 3 月) :除 Safari 外,2020 年 4 月之后发布的所有主要浏览器现在都支持此功能!

Edit (June 2021) : Both proposals are accepted by TC39 , the ECMAScript language committee, and Safari shipped this feature in version 14.1!编辑(2021 年 6 月) :两个提案都被TC39接受,ECMAScript 语言委员会和 Safari 在 14.1 版中发布了此功能!

In current drafts of ECMAScript 6 (as of February 2015), all class properties must be methods, not values (note in ECMAScript a "property" is similar in concept to an OOP field, except the field value must be a Function object, not any other value such as a Number or Object ).在 ECMAScript 6 的当前草案中(截至 2015 年 2 月),所有类属性都必须是方法,而不是值(注意在 ECMAScript 中,“属性”在概念上类似于 OOP 字段,但字段值必须是Function对象,而不是任何其他值,例如NumberObject )。

You can still specify these using traditional ECMAScript constructor property specifiers:您仍然可以使用传统的 ECMAScript 构造函数属性说明符指定这些:

 class Agent {
 }
 Agent.CIRCLE = 1;
 Agent.SQUARE = 2;
 ...

To get full advantage of static variable I followed this approach.为了充分利用静态变量,我遵循了这种方法。 To be more specific, we can use it to use private variable or having only public getter, or having both getter or setter.更具体地说,我们可以使用它来使用私有变量或只有公共 getter,或者同时拥有 getter 或 setter。 In the last case it's same as one of the solution posted above.在最后一种情况下,它与上面发布的解决方案之一相同。

var Url = (() => {
    let _staticMember = [];
    return class {
        static getQueries(hash = document.location.hash) {
            return hash;
        }

        static get staticMember(){
            return _staticMember;
        }
    };
})();

Usages:
console.log(Url.staticMember); // [];
Url.staticMember.push('it works');
console.log(Url.staticMember); // ['it works'];

I could create another class extending Url and it worked.我可以创建另一个扩展 Url 的类并且它起作用了。

I used babel to convert my ES6 code to ES5我使用 babel 将我的 ES6 代码转换为 ES5

@kangax 's answer does not imitate the whole static behaviour of traditional OOP language's, because you cannot access the static property by it's instance like const agent = new Agent; agent.CIRCLE; // Undefined @kangax 的回答并没有模仿传统 OOP 语言的整个静态行为,因为您无法通过它的实例访问静态属性,例如const agent = new Agent; agent.CIRCLE; // Undefined const agent = new Agent; agent.CIRCLE; // Undefined

If you want to access static property just like OOP's, here is my solution:如果您想像 OOP 一样访问静态属性,这是我的解决方案:

class NewApp {
  get MULTIPLE_VERSIONS_SUPPORTED() {
    return this.constructor.MULTIPLE_VERSIONS_SUPPORTED; // Late binding for inheritance
  }
}

NewApp.MULTIPLE_VERSIONS_SUPPORTED = true;

Test code as follows.测试代码如下。

 class NewApp { get MULTIPLE_VERSIONS_SUPPORTED() { console.log('this.constructor.name:', this.constructor.name); // late binding return this.constructor.MULTIPLE_VERSIONS_SUPPORTED; } } // Static property can be accessed by class NewApp.MULTIPLE_VERSIONS_SUPPORTED = true; const newApp = new NewApp; // Static property can be accessed by it's instances console.log('newApp.MULTIPLE_VERSIONS_SUPPORTED:', newApp.MULTIPLE_VERSIONS_SUPPORTED); // true // Inheritance class StandardApp extends NewApp {} // Static property can be inherited console.log('StandardApp.MULTIPLE_VERSIONS_SUPPORTED:', StandardApp.MULTIPLE_VERSIONS_SUPPORTED); // true // Static property can be overwritten StandardApp.MULTIPLE_VERSIONS_SUPPORTED = false; const std = new StandardApp; console.log('std.MULTIPLE_VERSIONS_SUPPORTED:', std.MULTIPLE_VERSIONS_SUPPORTED); // false

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM