本文翻译自:ES6 class variable alternatives
Currently in ES5 many of us are using the following pattern in frameworks to create classes and class variables, which is comfy: 当前在ES5中,我们许多人在框架中使用以下模式来创建类和类变量,这很方便:
// ES 5
FrameWork.Class({
variable: 'string',
variable2: true,
init: function(){
},
addItem: function(){
}
});
In ES6 you can create classes natively, but there is no option to have class variables: 在ES6中,您可以本地创建类,但是没有选择可以使用类变量:
// ES6
class MyClass {
const MY_CONST = 'string'; // <-- this is not possible in ES6
constructor(){
this.MY_CONST;
}
}
Sadly, the above won't work, as classes only can contain methods. 可悲的是,上述方法不起作用,因为类只能包含方法。
I understand that I can this.myVar = true
in constructor
…but I don't want to 'junk' my constructor, especially when I have 20-30+ params for a bigger class. 我知道我可以在constructor
this.myVar = true
…但是我不想“垃圾”我的构造函数,尤其是当我有20-30个以上的大型类参数时。
I was thinking of many ways to handle this issue, but haven't yet found any good ones. 我正在考虑解决该问题的多种方法,但尚未找到任何好的方法。 (For example: create a ClassConfig
handler, and pass a parameter
object, which is declared separately from the class. Then the handler would attach to the class. I was thinking about WeakMaps
also to integrate, somehow.) (例如:创建一个ClassConfig
处理程序,并传递一个与类分开声明的parameter
对象。然后该处理程序将附加到该类上。我在考虑将WeakMaps
也以某种方式进行集成。)
What kind of ideas would you have to handle this situation? 您将如何处理这种情况?
#1楼
参考:https://stackoom.com/question/1WWoR/ES-类变量替代
#2楼
2018 update: 2018更新:
There is now a stage 3 proposal - I am looking forward to make this answer obsolete in a few months. 现在有第3阶段的提案-我期待在几个月后使这个答案过时。
In the meantime anyone using TypeScript or babel can use the syntax: 同时,使用TypeScript或babel的任何人都可以使用以下语法:
varName = value
Inside a class declaration/expression body and it will define a variable. 在类声明/表达式主体内,它将定义一个变量。 Hopefully in a few months/weeks I'll be able to post an update. 希望在几个月/几周内,我将能够发布更新。
Update: Chrome 74 now ships with this syntax working. 更新:Chrome 74现在附带此语法。
The notes in the ES wiki for the proposal in ES6 ( maximally minimal classes ) note: ES Wiki中针对ES6中提案的注释( 最大类别 )注释:
There is (intentionally) no direct declarative way to define either prototype data properties (other than methods) class properties, or instance property 没有(有意地)没有直接的声明方式来定义原型数据属性(方法以外的类)或实例属性
Class properties and prototype data properties need be created outside the declaration. 类属性和原型数据属性需要在声明之外创建。
Properties specified in a class definition are assigned the same attributes as if they appeared in an object literal. 在类定义中指定的属性被赋予相同的属性,就像它们出现在对象文字中一样。
This means that what you're asking for was considered, and explicitly decided against. 这意味着您的要求已被考虑,并明确决定反对。
but... why? 但为什么?
Good question. 好问题。 The good people of TC39 want class declarations to declare and define the capabilities of a class. TC39的好人希望类声明声明和定义类的功能。 Not its members. 不是它的成员。 An ES6 class declaration defines its contract for its user. ES6类声明为其用户定义其合同。
Remember, a class definition defines prototype methods - defining variables on the prototype is generally not something you do. 请记住,类定义定义了原型方法-在原型上定义变量通常不是您要做的事情。 You can, of course use: 您当然可以使用:
constructor(){
this.foo = bar
}
In the constructor like you suggested. 在您建议的构造函数中。 Also see the summary of the consensus . 另请参见共识摘要 。
ES7 and beyond ES7及更高版本
A new proposal for ES7 is being worked on that allows more concise instance variables through class declarations and expressions - https://esdiscuss.org/topic/es7-property-initializers 对正在工作了ES7一项新的提案,通过类的声明和表达式可以更简洁的实例变量- https://esdiscuss.org/topic/es7-property-initializers
#3楼
What about the oldschool way? 那老式的方式呢?
class MyClass {
constructor(count){
this.countVar = 1 + count;
}
}
MyClass.prototype.foo = "foo";
MyClass.prototype.countVar = 0;
// ...
var o1 = new MyClass(2); o2 = new MyClass(3);
o1.foo = "newFoo";
console.log( o1.foo,o2.foo);
console.log( o1.countVar,o2.countVar);
In constructor you mention only those vars which have to be computed. 在构造函数中,您仅提及那些必须计算的变量。 I like prototype inheritance for this feature -- it can help to save a lot of memory(in case if there are a lot of never-assigned vars). 我喜欢此功能的原型继承-它可以帮助节省大量内存(以防万一有很多从未分配的var)。
#4楼
Just to add to Benjamin's answer — class variables are possible, but you wouldn't use prototype
to set them. 只是为了增加本杰明的答案-类变量是可能的,但是您不会使用prototype
来设置它们。
For a true class variable you'd want to do something like the following: 对于真正的类变量,您需要执行以下操作:
class MyClass {}
MyClass.foo = 'bar';
From within a class method that variable can be accessed as this.constructor.foo
(or MyClass.foo
). 在类方法中,可以使用this.constructor.foo
(或MyClass.foo
)访问变量。
These class properties would not usually be accessible from to the class instance. 这些类属性通常无法从类实例访问。 ie MyClass.foo
gives 'bar'
but new MyClass().foo
is undefined
即MyClass.foo
给'bar'
但new MyClass().foo
是undefined
If you want to also have access to your class variable from an instance, you'll have to additionally define a getter: 如果您还想从实例访问类变量,则必须另外定义一个getter:
class MyClass {
get foo() {
return this.constructor.foo;
}
}
MyClass.foo = 'bar';
I've only tested this with Traceur, but I believe it will work the same in a standard implementation. 我仅使用Traceur进行了此测试,但我相信它在标准实现中将发挥相同的作用。
JavaScript doesn't really have classes . JavaScript确实没有类 。 Even with ES6 we're looking at an object- or prototype-based language rather than a class-based language. 即使使用ES6,我们也在研究基于对象或原型的语言,而不是基于类的语言。 In any function X () {}
, X.prototype.constructor
points back to X
. 在任何function X () {}
, X.prototype.constructor
指向X
When the new
operator is used on X
, a new object is created inheriting X.prototype
. 在X
上使用new
运算符时,将继承X.prototype
创建一个新对象。 Any undefined properties in that new object (including constructor
) are looked up from there. 从那里查找该新对象(包括constructor
)中所有未定义的属性。 We can think of this as generating object and class properties. 我们可以将其视为生成对象和类的属性。
#5楼
This is a bit hackish combo of static and get works for me 这是一点静态的静态组合,可以为我工作
class ConstantThingy{
static get NO_REENTER__INIT() {
if(ConstantThingy._NO_REENTER__INIT== null){
ConstantThingy._NO_REENTER__INIT = new ConstantThingy(false,true);
}
return ConstantThingy._NO_REENTER__INIT;
}
}
elsewhere used 其他地方使用
var conf = ConstantThingy.NO_REENTER__INIT;
if(conf.init)...
#6楼
In your example: 在您的示例中:
class MyClass {
const MY_CONST = 'string';
constructor(){
this.MY_CONST;
}
}
Because of MY_CONST is primitive https://developer.mozilla.org/en-US/docs/Glossary/Primitive we can just do: 由于MY_CONST是原始https://developer.mozilla.org/en-US/docs/Glossary/Primitive,因此我们可以执行以下操作:
class MyClass {
static get MY_CONST() {
return 'string';
}
get MY_CONST() {
return this.constructor.MY_CONST;
}
constructor() {
alert(this.MY_CONST === this.constructor.MY_CONST);
}
}
alert(MyClass.MY_CONST);
new MyClass
// alert: string ; true
But if MY_CONST
is reference type like static get MY_CONST() {return ['string'];}
alert output is string, false . 但是,如果MY_CONST
是引用类型(如static get MY_CONST() {return ['string'];}
警报输出为string,为false 。 In such case delete
operator can do the trick: 在这种情况下, delete
操作符可以解决问题:
class MyClass {
static get MY_CONST() {
delete MyClass.MY_CONST;
return MyClass.MY_CONST = 'string';
}
get MY_CONST() {
return this.constructor.MY_CONST;
}
constructor() {
alert(this.MY_CONST === this.constructor.MY_CONST);
}
}
alert(MyClass.MY_CONST);
new MyClass
// alert: string ; true
And finally for class variable not const
: 最后对于类变量不是const
:
class MyClass {
static get MY_CONST() {
delete MyClass.MY_CONST;
return MyClass.MY_CONST = 'string';
}
static set U_YIN_YANG(value) {
delete MyClass.MY_CONST;
MyClass.MY_CONST = value;
}
get MY_CONST() {
return this.constructor.MY_CONST;
}
set MY_CONST(value) {
this.constructor.MY_CONST = value;
}
constructor() {
alert(this.MY_CONST === this.constructor.MY_CONST);
}
}
alert(MyClass.MY_CONST);
new MyClass
// alert: string, true
MyClass.MY_CONST = ['string, 42']
alert(MyClass.MY_CONST);
new MyClass
// alert: string, 42 ; true