ES6类变量替代品

目前在ES5中,我们很多人在框架中使用以下模式来创建类和类变量,这很舒服:

// ES 5
FrameWork.Class({


variable: 'string',
variable2: true,


init: function(){


},


addItem: function(){


}


});

在ES6中,你可以在本地创建类,但是没有选项可以有类变量:

// ES6
class MyClass {
const MY_CONST = 'string'; // <-- this is not possible in ES6
constructor(){
this.MY_CONST;
}
}

遗憾的是,上述方法不起作用,因为类只能包含方法。

我知道我可以this.myVar = trueconstructor…但我不想“垃圾”我的构造函数,特别是当我有20-30+参数为一个更大的类。

我想了很多方法来处理这个问题,但还没有找到一个好的。(例如:创建一个ClassConfig处理程序,并传递一个parameter对象,该对象与类分开声明。然后处理程序将附加到类。我也在考虑WeakMaps的积分,以某种方式。)

你会有什么样的想法来处理这种情况?

496838 次浏览

2018年更新:

现在有一个第三阶段的提案——我期待着在几个月内让这个答案过时。

与此同时,任何使用TypeScript或babel的人都可以使用以下语法:

varName = value

在类声明/表达式体中,它将定义一个变量。希望在几个月或几周后,我能够发布更新。

更新:Chrome 74现在船与此语法工作。


ES wiki中关于ES6 (最大最小类)提案的注释:

(有意地)没有直接的声明性方法来定义原型数据属性(方法除外)类属性或实例属性

类属性和原型数据属性需要在声明之外创建。

在类定义中指定的属性被赋予与出现在对象字面量中相同的属性。

这意味着你的要求已经被考虑过了,而且被明确否决了。 . #

但是…为什么?

好问题。TC39的好人希望类声明能够声明和定义类的功能。不是它的成员。ES6类声明为用户定义了它的契约。

记住,类定义定义了原型方法——在原型上定义变量通常不是你要做的事情。 当然,你可以使用:

constructor(){
this.foo = bar
}

在构造函数中。参见共识总结

ES7及以上版本

ES7的一个新提议正在研究中,它允许通过类声明和表达式来实现更简洁的实例变量——https://esdiscuss.org/topic/es7-property-initializers

那老派的方法呢?

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);
在构造函数中,你只提到那些必须计算的变量。 我喜欢这个特性的原型继承——它可以帮助节省大量内存(如果有很多从未分配的vars)

只是补充一下本杰明的答案——类变量是可能的,但你不会使用prototype来设置它们。

对于一个真正的类变量,你应该像下面这样做:

class MyClass {}
MyClass.foo = 'bar';

在类方法中,可以以this.constructor.foo(或MyClass.foo)的形式访问该变量。

这些类属性通常不能被类实例访问。例如,MyClass.foo'bar',而new MyClass().fooundefined

如果你也想从实例中访问你的类变量,你必须另外定义一个getter:

class MyClass {
get foo() {
return this.constructor.foo;
}
}


MyClass.foo = 'bar';

我只在Traceur上测试过这个功能,但我相信它在标准实现中也能发挥同样的作用。

< p > # EYZ3。即使在ES6中,我们看到的是基于对象或原型的语言,而不是基于类的语言。在任何function X () {}中,X.prototype.constructor指向X。 当在X上使用new操作符时,将创建一个继承X.prototype的新对象。新对象中的任何未定义的属性(包括constructor)都将从那里查找。我们可以认为这是生成对象和类属性

这是一个有点黑客的组合静态和得到工作为我

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

在其他地方使用

var conf = ConstantThingy.NO_REENTER__INIT;
if(conf.init)...

在你的例子中:

class MyClass {
const MY_CONST = 'string';
constructor(){
this.MY_CONST;
}
}

因为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

但如果MY_CONST是引用类型,如static get MY_CONST() {return ['string'];},则警报输出为字符串,假。在这种情况下,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

最后对于类变量不是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

正如Benjamin在他的回答中所说,TC39明确决定至少在ES2015中不包含这个功能。然而,共识似乎是他们将在ES2016中添加它。

语法还没有决定,但是ES2016有一个初步的建议,它允许你在一个类上声明静态属性。

多亏了巴别塔的魔力,你今天可以使用它。根据这些指令启用类属性转换,就可以开始了。下面是一个语法示例:

class foo {
static myProp = 'bar'
someFunction() {
console.log(this.myProp)
}
}

这个建议还处于非常早期的状态,所以准备好随着时间的推移调整您的语法。

由于您的问题主要是风格上的(不想用一堆声明填充构造函数),因此也可以从风格上解决。

在我看来,许多基于类的语言的构造函数都是以类名本身命名的。在风格上,我们可以使用它来创建一个ES6类,它在风格上仍然有意义,但不会将构造函数中发生的典型操作与我们正在做的所有属性声明组合在一起。我们简单地使用实际的JS构造函数作为“声明区域”,然后创建一个名为function的类,否则我们将其视为“其他构造函数东西”区域,在真正的构造函数的末尾调用它。

"use strict";


class MyClass
{
// only declare your properties and then call this.ClassName(); from here
constructor(){
this.prop1 = 'blah 1';
this.prop2 = 'blah 2';
this.prop3 = 'blah 3';
this.MyClass();
}


// all sorts of other "constructor" stuff, no longer jumbled with declarations
MyClass() {
doWhatever();
}
}

在构造新实例时,两者都将被调用。

有点像有两个构造函数,你可以把声明和其他你想要执行的构造函数动作分开,并且在风格上让它不太难以理解这是在发生什么。

我发现当处理大量声明和/或大量需要在实例化时发生的操作并希望保持这两种思想彼此不同时,使用这种风格很好。


请注意:我故意不使用典型的“初始化”方法(如init()initialize()方法),因为它们的使用方式经常不同。在构造和初始化的概念之间有一种假定的区别。使用构造函数时,人们知道它们作为实例化的一部分被自动调用。看到init方法,许多人会毫不犹豫地认为他们需要按照var mc = MyClass(); mc.init();的形式做一些事情,因为这是通常的初始化方式。我不是试图为类的用户添加初始化过程,我试图添加类本身的构造过程。

虽然有些人可能会多看一眼,但这实际上是关键所在:它传达给他们的意图是建筑的一部分,即使让他们有点反应迟钝,“这不是ES6构造函数是如何工作的”,第二看实际的构造函数去“哦,他们称之为底部,我看到“,这是远比不交流意图(或不正确的沟通),可能让很多人使用它错了,试图从外部初始化它和垃圾。这就是我所建议的模式。


对于那些不想遵循这种模式的人来说,完全相反的方法也可以奏效。在开始时将声明传递给另一个函数。也许可以命名为“properties”或“publicProperties”之类的。然后把剩下的东西放在普通构造函数中。

"use strict";


class MyClass
{
properties() {
this.prop1 = 'blah 1';
this.prop2 = 'blah 2';
this.prop3 = 'blah 3';
}


constructor() {
this.properties();
doWhatever();
}
}

请注意,第二个方法可能看起来更干净,但它也有一个固有的问题,当使用该方法的类扩展另一个类时,properties会被覆盖。为了避免这种情况,您必须为properties提供更唯一的名称。我的第一个方法没有这个问题,因为它的伪构造函数的一半是以类唯一命名的。

巴别塔支持ESNext中的类变量,检查这个例子:

class Foo {
bar = 2
static iha = 'string'
}


const foo = new Foo();
console.log(foo.bar, foo.iha, Foo.bar, Foo.iha);
// 2, undefined, undefined, 'string'

我解决这个问题的方法,这是另一个选择(如果你有jQuery可用),是在一个老式的对象中定义字段,然后用该对象扩展类。我也不想在构造函数中添加赋值,这似乎是一个整洁的解决方案。

function MyClassFields(){
this.createdAt = new Date();
}


MyClassFields.prototype = {
id : '',
type : '',
title : '',
createdAt : null,
};


class MyClass {
constructor() {
$.extend(this,new MyClassFields());
}
};

——根据Bergi的评论更新。

没有JQuery版本:

class SavedSearch  {
constructor() {
Object.assign(this,{
id : '',
type : '',
title : '',
createdAt: new Date(),
});


}
}

你仍然会得到“胖”构造函数,但至少它都在一个类中,并在一次命中赋值。

<强>编辑# 2: 我现在走了一个完整的循环,现在在构造函数中赋值,例如

class SavedSearch  {
constructor() {
this.id = '';
this.type = '';
this.title = '';
this.createdAt = new Date();
}
}

为什么?真的很简单,使用上面的加上一些JSdoc注释,PHPStorm就能够在属性上执行代码补全。在一次命中分配所有的变量是很好的,但无法编码完成属性,在我看来,不值得(几乎肯定是微乎其微的)性能收益。

你可以模仿es6类的行为…并使用你的类变量:)

看妈妈……没有课!

// Helper
const $constructor = Symbol();
const $extends = (parent, child) =>
Object.assign(Object.create(parent), child);
const $new = (object, ...args) => {
let instance = Object.create(object);
instance[$constructor].call(instance, ...args);
return instance;
}
const $super = (parent, context, ...args) => {
parent[$constructor].call(context, ...args)
}
// class
var Foo = {
classVariable: true,


// constructor
[$constructor](who){
this.me = who;
this.species = 'fufel';
},


// methods
identify(){
return 'I am ' + this.me;
}
}


// class extends Foo
var Bar = $extends(Foo, {


// constructor
[$constructor](who){
$super(Foo, this, who);
this.subtype = 'barashek';
},


// methods
speak(){
console.log('Hello, ' + this.identify());
},
bark(num){
console.log('Woof');
}
});


var a1 = $new(Foo, 'a1');
var b1 = $new(Bar, 'b1');
console.log(a1, b1);
console.log('b1.classVariable', b1.classVariable);

我把它放在GitHub

[长线程,不确定是否已列为选项…].
. 简单的替代替换contsants只,将在类外部定义const。 除非附带getter,否则只能从模块本身访问 这样prototype就不会被丢弃,你会得到const.

// will be accessible only from the module itself
const MY_CONST = 'string';
class MyClass {


// optional, if external access is desired
static get MY_CONST(){return MY_CONST;}


// access example
static someMethod(){
console.log(MY_CONST);
}
}

你可以在构造函数中声明变量。

class Foo {
constructor() {
var name = "foo"
this.method = function() {
return name
}
}
}


var foo = new Foo()


foo.method()

ES7类成员语法:

ES7有一个“废弃”构造函数的解决方案。这里有一个例子:

class Car {
  

wheels = 4;
weight = 100;


}


const car = new Car();
console.log(car.wheels, car.weight);

上面的例子在ES6中看起来如下所示:

class Car {


constructor() {
this.wheels = 4;
this.weight = 100;
}


}


const car = new Car();
console.log(car.wheels, car.weight);

使用此语法时请注意,此语法可能不是所有浏览器都支持,可能必须转译为早期版本的JS。

好处:一个对象工厂:

function generateCar(wheels, weight) {


class Car {


constructor() {}


wheels = wheels;
weight = weight;


}


return new Car();


}




const car1 = generateCar(4, 50);
const car2 = generateCar(6, 100);


console.log(car1.wheels, car1.weight);
console.log(car2.wheels, car2.weight);

你仍然不能像在其他编程语言中那样声明任何类。但是您可以创建尽可能多的类变量。但问题是类对象的范围。所以根据我,最好的方法OOP编程在ES6 Javascript:-

class foo{
constructor(){
//decalre your all variables
this.MY_CONST = 3.14;
this.x = 5;
this.y = 7;
// or call another method to declare more variables outside from constructor.
// now create method level object reference and public level property
this.MySelf = this;
// you can also use var modifier rather than property but that is not working good
let self = this.MySelf;
//code .........
}
set MySelf(v){
this.mySelf = v;
}
get MySelf(v){
return this.mySelf;
}
myMethod(cd){
// now use as object reference it in any method of class
let self = this.MySelf;
// now use self as object reference in code
}
}

如果它只是混乱的问题,在constructor为什么不实现一个initialize方法,intializes变量。当构造函数充满了不必要的东西时,这样做是很正常的。即使在类型化的程序语言中,如C#,通常也会添加Initialize方法来处理。

只需定义一个getter

class MyClass
{
get MY_CONST () { return 'string'; }


constructor ()
{
console.log ("MyClass MY_CONST:", this.MY_CONST);
}
}


var obj = new MyClass();

截至2021年的最新浏览器(不是IE,请参阅MDN浏览器图表)实现了公共类字段,这似乎是你正在寻找的:

class MyClass {
static foo = 3;
}


console.log(MyClass.foo);

然而,显然这是不可能使const: 在ES6类中声明静态常量?

静态getter看起来非常接近:

class MyClass {
static get CONST() {
return 3;
}
}


MyClass.CONST = 4; // property unaffected
console.log(MyClass.CONST);