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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28445693/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 01:47:53  来源:igfitidea点击:

How do I make a "public static field" in an ES6 class?

javascriptecmascript-6

提问by aebabis

I'm making a Javascript class and I'd like to have a public static field like in Java. This is the relevant code:

我正在制作一个 Javascript 类,我想要一个像 Java 一样的公共静态字段。这是相关代码:

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. Is there a way to get the desired behavior or do I have to write a getter?

看起来 ES6 模块不允许这样做。有没有办法获得所需的行为,还是我必须编写一个吸气剂?

回答by kangax

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]
  staticMethodDefinition[?Yield] ;

ClassElement[Yield] :
  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. Specifically, step 20:

所以从那里你可以遵循14.5.14— 运行时语义:ClassDefinitionEvaluation — 仔细检查它是否真的像它看起来那样做。具体来说,步骤20:

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

IsStatic is defined earlier in 14.5.9

IsStatic在早期定义14.5.9

ClassElement : static MethodDefinition
Return true.

ClassElement : 静态 MethodDefinition
返回 true。

So PropertyMethodDefinitionis called with "F" (constructor, function object) as an argument, which in its turn creates an accessor method on that object.

SoPropertyMethodDefinition以“F”(构造函数,函数对象)作为参数调用,它反过来在该对象上创建一个访问器方法

This already worksin at least IETP (tech preview), as well as 6to5 and Traceur compilers.

这至少已经在 IETP(技术预览版)以及 6to5 和 Traceur 编译器中起作用。

回答by Timothy Gu

There is a Stage 3 ECMAScript proposal called "Static Class Features"by Daniel Ehrenberg and Jeff Morrison that aims to solve this problem. Along with the Stage 3 "Class Fields"proposal, future code will look like this:

丹尼尔·埃伦伯格 (Daniel Ehrenberg) 和杰夫·莫里森 (Jeff Morrison)提出了一个名为“静态类特征”的第 3 阶段 ECMAScript 提案,旨在解决这个问题。随着第 3 阶段的“类字段”提案,未来的代码将如下所示:

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;

Babelsupportstranspiling class fields through @babel/plugin-proposal-class-properties(included in the stage-3 preset), so that you can use this feature even if your JavaScript runtime doesn't support it.

Babel支持通过@babel/plugin-proposal-class-properties(包含在stage-3 预设中)转换类字段,这样即使您的 JavaScript 运行时不支持它,您也可以使用此功能。



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 的解决方案相比,该解决方案的性能也更高,因为这里的属性是直接访问的,而不是通过调用函数来访问的。

If this proposal gets accepted, then it will be possible to write JavaScript code in a way that's more similar to traditional object-oriented languages like Java and C?.

如果这个提议被接受,那么将有可能以更类似于 Java 和 C 等传统面向对象语言的方式编写 JavaScript 代码。



Edit: A unified class fields proposal is now at stage 3; update to Babel v7.x packages.

编辑:统一的类字段提案现在处于第 3 阶段;更新到 Babel v7.x 包。

Edit (Feb 2020): The static class features have been split out into a different proposal. Thanks @GOTO0!

编辑(2020 年 2 月):静态类功能已拆分为不同的提案。谢谢@GOTO0!

回答by Dai

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 Functionobject, not any other value such as a Numberor Object).

在 ECMAScript 6 的当前草案中(截至 2015 年 2 月),所有类属性必须是方法,而不是值(注意在 ECMAScript 中,“属性”在概念上类似于 OOP 字段,但字段值必须是Function对象,而不是任何其他值,例如 aNumberObject)。

You can still specify these using traditional ECMAScript constructor property specifiers:

您仍然可以使用传统的 ECMAScript 构造函数属性说明符指定这些:

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

回答by SM Adnan

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. In the last case it's same as one of the solution posted above.

为了充分利用静态变量,我遵循了这种方法。更具体地说,我们可以使用它来使用私有变量或只有公共 getter,或者同时拥有 getter 或 setter。在最后一种情况下,它与上面发布的解决方案之一相同。

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

回答by legend80s

@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

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