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


86

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

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

这是我得到的错误:

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

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


您正在使用哪种ECMAScript 6引擎实现?

Answers:


136

您使用访问器和“ static”关键字创建“ public static field”:

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:static MethodDefinition
返回true。

因此PropertyMethodDefinition以“ F”(构造函数,函数对象)作为参数进行调用,这又在该对象上创建了一个访问器方法

已经工作至少IETP(技术预览版),以及6to5和Traceur编译器。


对于其他人,Node尚不支持静态访问器属性。:-/ kangax.github.io/compat-table/es6/…–
David Hernandez

1
从至少Node.js 6.x +开始,此功能受支持。
NuSkooler

请注意,如果您使用流,则必须unsafe.enable_getters_and_setters=true在.flowconfig下添加一行[options](这很烦人)。
克里斯蒂娜

这对我不起作用,我得到```未处理的拒绝TypeError:无法设置类Collections {api_1 |的属性dataHashKey。静态获取dataHashKey(){api_1 | 返回“收藏”;api_1 | }`
Pavan

54

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

以上等同于:

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;

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


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

如果该建议被接受,那么将有可能以与传统的面向对象语言(如Java和C♯)更相似的方式编写JavaScript代码。


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

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


我认为相关建议实际上就是这一建议(静态类功能)。
GOTO

29

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

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

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

11
请注意,class无论如何,ES6语法只是传统JS构造函数和原型的语法糖。
马特·布朗

我认为您希望将这些属性放在原型上,而不是在构造函数上,以便通过实例的属性引用显示它们。
尖尖的2015年

@Pointy我推断OP正在尝试存储常量以供参考(几乎像C#/。NET一样enum)。

2
@MattBrowne是的,但是要明确一点,class语法也有一些细微的差别。例如,用声明的方法Class.prototype.method = function () {};可枚举的(在for-in循环中可见),而class方法是不可枚举的。
蒂莫西·古

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


1
什么是“全面优势”?会不会class Url { static getQueries… }; Url.staticMember = [];简单得多?
Bergi

这些===比较都产生了收益false,顺便说一句
Bergi 2015年

“全部优势”表示,如果需要,您可以通过以上方式将_staticMember保留为私有。
SM Adnan

-1

@kangax的答案不会模仿传统OOP语言的整个静态行为,因为您无法通过静态实例的实例来访问static属性 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


1
static通过实例访问字段是很不常见的,不是吗?在某些语言(例如Java)中,如果您这样做,IDE实际上会发出警告/提示。
Isac '18

@Isac是的,您是对的。不鼓励按实例访问,我的回答也是如此。解决方案的另一个角度。😀
legend80s
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.