如何在 ES6 类中创建“公共静态字段”?
Posted
技术标签:
【中文标题】如何在 ES6 类中创建“公共静态字段”?【英文标题】:How do I make a "public static field" in an ES6 class? 【发布时间】:2015-04-11 07:30:42 【问题描述】:我正在制作一个 javascript 类,我想要一个像 Java 一样的公共静态字段。这是相关代码:
export default class Agent
CIRCLE: 1,
SQUARE: 2,
...
这是我得到的错误:
line 2, col 11, Class properties must be methods. Expected '(' but instead saw ':'.
看起来 ES6 模块不允许这样做。有没有办法获得所需的行为,还是我必须编写一个 getter?
【问题讨论】:
您使用的是哪个 ECMAScript 6 引擎实现? @Dai github.com/ModuleLoader/es6-module-loader 【参考方案1】:您使用访问器和“静态”关键字创建“公共静态字段”:
class Agent
static get CIRCLE()
return 1;
static get SQUARE()
return 2;
Agent.CIRCLE; // 1
查看规范,14.5 — 类定义 — 你会看到一些可疑的相关内容 :)
类元素[产量]: MethodDefinition[?Yield]静态 MethodDefinition[?Yield] ;
因此,您可以从那里关注14.5.14 — 运行时语义:ClassDefinitionEvaluation — 仔细检查它是否真的像看起来那样做。具体来说,第 20 步:
对于每个 ClassElement m 按方法顺序排列
如果 IsStatic of m 为假,则
让 status 为使用参数 proto 和 false 对 m 执行 PropertyDefinitionEvaluation 的结果。
否则,设 status 为对 m 执行 PropertyDefinitionEvaluation 的结果,参数为 F 和 false。
如果状态是突然完成,则将正在运行的执行上下文的 LexicalEnvironment 设置为 lex。 返回状态。
IsStatic 在前面的 14.5.9 中定义
类元素:静态方法定义 返回真。
所以PropertyMethodDefinition
是用“F”(构造函数,函数对象)作为参数调用的,而它又是creates an accessor method on that object。
这个already works 至少在 IETP(技术预览)以及 6to5 和 Traceur 编译器中。
【讨论】:
对于其他人来说,Node 尚不支持静态访问器属性。 :-/ kangax.github.io/compat-table/es6/… 至少从 Node.js 6.x+ 开始,这是受支持的。 请注意,如果您使用流,则必须在[options]
下的.flowconfig 中添加一行unsafe.enable_getters_and_setters=true
(这很烦人)。
这对我不起作用我得到```未处理的拒绝类型错误:无法设置类集合的属性 dataHashKey api_1 |静态获取 dataHashKey() api_1 |返回“收藏”; api_1 | ```
Kangax,也许我对此有误,但我认为您是 Fabric 背后的开发人员,或者至少您非常精通 Fabric。我有两个未解决的问题需要解决才能继续我的项目。你能这么好心看看他们吗?【参考方案2】:
从 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 等人的 "Static Class Features" 和 "Class Fields" 提案中添加的。 Google Chrome(和新 Edge)开始支持 version 72 中的这两个提案,相当于 Node.js 12+。 Firefox 从version 69 开始支持公共实例字段,从version 75 开始支持静态实例字段。自version 14.1 以来,Safari 都支持。在caniuse.com 上查看更多信息。
对于尚不支持这些功能的旧版浏览器,您可以使用Babel 到transpile 类字段。这需要启用@babel/plugin-proposal-class-properties(默认在@babel/plugin-env 中从v7.14.0 开始启用)。
与@kangax 声明getter 的解决方案相比,该解决方案还可以更高效,因为这里的属性是直接访问的,而不是通过调用函数。
编辑:统一类字段提案现在处于第 3 阶段。
编辑(2020 年 2 月):静态类功能已拆分为不同的提案。谢谢@GOTO0!
编辑(2021 年 3 月):除了 Safari,2020 年 4 月之后发布的所有主要浏览器现在都支持此功能!
编辑(2021 年 6 月):这两个提案都被 ECMAScript 语言委员会 TC39 接受,Safari 在 14.1 版中提供了此功能!
【讨论】:
我觉得相关提案其实是this one(静态类特性)。【参考方案3】:在当前的 ECMAScript 6 草案中(截至 2015 年 2 月),所有类属性都必须是方法,而不是值(注意在 ECMAScript 中,“属性”在概念上类似于 OOP 字段,除了字段值必须是 @ 987654321@ 对象,而不是任何其他值,例如 Number
或 Object
)。
您仍然可以使用传统的 ECMAScript 构造函数属性说明符指定这些:
class Agent
Agent.CIRCLE = 1;
Agent.SQUARE = 2;
...
【讨论】:
请注意,ES6class
语法只是传统 JS 构造函数和原型的语法糖。
我认为您希望将这些属性放在原型上,而不是放在构造函数上,以便通过实例的属性引用可以看到它们。
@Pointy 我推断 OP 正在尝试存储常量以供参考(几乎就像 C#/.NET enum
)。
@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
【讨论】:
什么是“充分优势”?class Url static getQueries… ; Url.staticMember = [];
不会简单得多吗?
那些===
比较都产生false
,顺便说一句
"Full Advantage" 的意思是,在上述方式中,您可以根据需要将 _staticMember 设为私有。【参考方案5】:
@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
【讨论】:
通过实例访问static
字段是相当少见的,你不是说吗?在某些语言中,例如 Java,如果您执行类似的操作,IDE 实际上会发出警告/提示。
@Isac 是的,你是对的。不鼓励通过实例访问,我的回答也是如此。只是解决方案的另一个角度。 ?以上是关于如何在 ES6 类中创建“公共静态字段”?的主要内容,如果未能解决你的问题,请参考以下文章