91

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

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

这是我得到的错误:

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

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

4

5 回答 5

142

您使用访问器和“静态”关键字创建“公共静态字段”:

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

Agent.CIRCLE; // 1

查看规范,14.5 - 类定义 - 你会看到一些可疑的相关内容:)

ClassElement[Yield] :
  MethodDefinition[?Yield]
  静态MethodDefinition[?Yield] ;

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

  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 早在 14.5.9 中定义

ClassElement : 静态 MethodDefinition
返回真。

SoPropertyMethodDefinition以“F”(构造函数,函数对象)作为参数调用,进而在该对象上创建访问器方法

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

于 2015-02-11T12:21:45.933 回答
59

从 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'
    }
}

以上等价于:

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;

Daniel Ehrenberg 等人在“静态类特征”“类字段”提案中添加了这些特征。Google Chrome(和新的 Edge)在版本 72中开始支持这两个提议,相当于Node.js 12+。Firefox 从69 版开始支持公共实例字段,从75版开始支持静态实例字段。Safari 从14.1 版本开始支持两者。在caniuse.com上查看更多信息。

对于尚不支持这些功能的旧浏览器,您可以使用Babel转译字段。这需要启用@babel/plugin-proposal-class-properties(从v7.14.0开始在 @babel/plugin-env 中默认启用)。


与@kangax 的声明 getter 的解决方案相比,该解决方案还可以提高性能,因为这里直接访问属性而不是通过调用函数。


编辑:统一类字段提案现在处于第 3 阶段。

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

编辑(2021 年 3 月):除了 Safari,2020 年 4 月之后发布的所有主要浏览器现在都支持此功能!

编辑(2021 年 6 月):这两个提案都被ECMAScript 语言委员会TC39接受,Safari 在 14.1 版中发布了这个功能!

于 2016-08-02T18:18:07.673 回答
29

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

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

 class Agent {
 }
 Agent.CIRCLE = 1;
 Agent.SQUARE = 2;
 ...
于 2015-02-11T02:53:09.193 回答
4

为了充分利用静态变量,我遵循了这种方法。更具体地说,我们可以使用它来使用私有变量或只有公共 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'];

我可以创建另一个扩展 Url 的类并且它有效。

我使用 babel 将我的 ES6 代码转换为 ES5

于 2015-07-06T00:05:23.410 回答
-1

@kangax 的答案并没有模仿传统 OOP 语言的整个静态行为,因为您无法通过它的实例访问静态属性,例如const agent = new Agent; agent.CIRCLE; // Undefined

如果您想像 OOP 一样访问静态属性,这是我的解决方案:

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

NewApp.MULTIPLE_VERSIONS_SUPPORTED = true;

测试代码如下。

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

于 2017-03-17T09:04:33.170 回答