当前,在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 = true中使用constructor…但是我不想“垃圾”我的构造函数,尤其是当我有20-30个以上的参数用于较大的类时。

我在考虑解决此问题的多种方法,但尚未找到任何好的方法。 (例如:创建一个ClassConfig处理程序,并传递一个与类分开声明的parameter对象。然后,该处理程序将附加到该类上。我正在考虑将WeakMaps也集成到某种程度上。)

您将如何处理这种情况?

评论

您的主要问题是您将在构造函数中使用20-30个参数重复执行this.member = member?

您不能只在类下使用public variable2 = true吗?这将在原型上对其进行定义。

@ΘεόφιλοςΜουρατίδης:是的,我也想将构造函数用于初始化过程而不是变量声明。

@derylius:这是主要问题,它没有此功能。 ES6草案中甚至还没有决定公共/私有用途。试试看:es6fiddle.net

根据最新消息,它具有以下功能:wiki.ecmascript.org/doku.php?id=harmony:classes

#1 楼

2018年更新:
现在有了第3阶段的提案-我希望在几个月后使这个答案过时。
同时,任何使用TypeScript或babel的人都可以使用以下语法:
varName = value

在类声明/表达式主体内部,它将定义一个变量。希望能在几个月/几周内发布更新。
更新:Chrome 74现在可以使用此语法。

ES Wiki中有关该建议的注释ES6(最大类数)注意:

(有意)没有直接声明方式来定义原型数据属性(除方法外)类属性或实例属性
类属性和原型数据属性需要在声明之外创建。
在类定义中指定的属性将被赋予与在对象文字中出现的属性相同的属性。

这意味着您要的是被考虑并明确决定反对。
...为什么?
好问题。 TC39的好人希望类声明声明和定义类的功能。不是它的成员。 ES6类声明为其用户定义其契约。
请记住,类定义定义了原型方法-在原型上定义变量通常是您不需要做的事情。
当然,您可以使用:
constructor(){
    this.foo = bar
}

在您建议的构造函数中。另请参见共识摘要。
ES7及更高版本
正在为ES7制定一项新提案,该提案允许通过类声明和表达式获得更简洁的实例变量-https://esdiscuss.org/topic/ es7-property-initializers

评论


@wintercounter重要的是,允许定义属性将像方法一样在原型上定义属性,而不是在每个实例上进行定义。最大最小类仍然是其核心原型继承。在这种情况下,您真正​​想做的是共享结构并为每个实例分配成员。这根本不是ES6中的类的目标-共享功能。所以是的,对于共享结构,您必须坚持旧的语法。至少直到ES7 :)

–本杰明·格伦鲍姆(Benjamin Gruenbaum)
2014年4月11日在8:26

您可能要提及静态属性

–贝尔吉
15年2月26日在22:57

哎呀,脑子放屁。我忘了静态也只适用于方法。

–贝尔吉
15年2月26日在23:07

如果TC39的优秀人才不希望它的行为像其他编程界期望的那样,那么他们应该用“类”以外的名称来命名。

– Alex
16年1月4日在17:59

另请参见“类字段和静态属性”建议(已在Babel中实现:github.com/jeffmo/es-class-fields-and-static-properties

–马特·布朗(Matt Browne)
16 Apr 16'0:57

#2 楼

只是为了增加本杰明的答案-类变量是可能的,但您不会使用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进行过测试,但是我相信它在标准实现中也可以使用。

JavaScript确实没有类。即使使用ES6,我们也在研究基于对象或原型的语言,而不是基于类的语言。在任何function X () {}中,X.prototype.constructor都指向X
当在new上使用X运算符时,将继承X.prototype来创建一个新对象。从那里查找该新对象(包括constructor)中所有未定义的属性。我们可以将其视为生成对象和类属性。

#3 楼

Babel在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'


评论


您可以通过在bar前面加上“#”来使它成为私有类变量:#bar = 2;

–Lonnie Best
18-09-22在3:07



#4 楼

在您的示例中:

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'];}的引用类型,则警报输出为字符串,则为false。在这种情况下,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


评论


如果出于性能原因,请避免单独使用delete运算符。您实际上想要的是Object.defineProperty。

–贝尔吉
2015年10月23日下午4:02

@shinzou我在想同样的事情。不过,不一定是OP的错;实际上,这是一种语言,缺乏以反映其真实世界关系的方式来表示数据的适当机制。

–user1974458
19年8月25日在19:36

#5 楼

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

我的看法是,许多基于类的语言都将构造函数作为以类名本身命名的函数。从风格上讲,我们可以使用它来使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();
    }
}


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

Sorta就像有两个构造函数,在其中分离了声明和要执行的其他构造函数,而且从风格上讲,这也不难理解发生了什么。

我发现在处理许多声明和/或需要执行的许多动作时,使用它是一种不错的样式。实例化并希望使这两种想法彼此不同。


注意:我非常有目的地不使用“初始化”的典型惯用想法(例如init()initialize()方法),因为这些经常被不同地使用。构造和初始化之间存在某种假定的差异。与构造函数一起工作的人们知道,它们在实例化过程中被自动调用。看到init方法后,许多人会不假思索地假设他们需要按照var mc = MyClass(); mc.init();的形式进行操作,因为这通常是您初始化的方式。我不是要为类的用户添加初始化过程,而是要添加到类本身的构造过程中。

尽管有些人可能会花一会儿时间,但这实际上是重点:它向他们传达了意图是建设的一部分,即使这会使他们做一番重复即走。“那不是ES6构造函数的工作原理”,然后再看一下实际的构造函数,“哦,我知道它们是在底部”,这比不传达意图(或错误传达)要好得多,人们使用它是错误的,试图从外部和垃圾中初始化它。这对我建议的模式非常有意。


对于那些不想遵循该模式的人,完全相反的做法也可以。在开始时将声明植入另一个函数。也许将其命名为“属性”或“ publicProperties”或其他名称。然后将其余内容放入普通的构造函数中。

"use strict";

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

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


请注意,第二种方法看起来更干净,但它也存在一个固有的问题,即properties被一个方法覆盖使用此方法的类扩展了另一个。为避免这种情况,您必须给properties赋予更多唯一的名称。我的第一个方法没有这个问题,因为其虚假的构造函数的一半以该类唯一命名。

评论


请不要使用以类本身命名的原型方法。在JS中这不是惯用语言,请不要尝试使其看起来像另一种语言。如果您确实要使用此方法,则该方法的规范名称为init。

–贝尔吉
15/12/22在12:13

@Bergi-init是一种经常使用的模式,通常意味着在外部用户想要初始化时从类外部调用它,即var b = new Thing(); b.init();。这是100%的风格选择,我更愿意传达的是,这是第二个函数,可以利用其他语言中的模式自动调用它。有人看待并假设他们需要从外部调用MyClass方法的可能性要小得多,他们更有可能意识到意图是构造中起作用的第二种方法(即在实例化时自行调用)。

–Jimbo Jonny
2015年12月22日在16:20



嗯,通过查看构造函数,我可能会意识到这一点,而不是通过方法名称MyClass来实现。

–贝尔吉
15年12月22日在19:05

@Bergi-从另一种语言获取一种模式并将其应用于JS的方式在技术上不是正在发生的事情,但是仍然可以正常工作并不是没有先例的。您不能告诉我,没有人注意到$ myvar指向用于保存jQuery对象的变量的标准方式不方便地类似于许多PHP程序员习惯在变量开头使用$的模式。只是有点暗示:“是的,这不是完全相同的东西……但是看起来……它仍然是变量,因为这是在某些语言中完成变量的一种方式!”帮助。

–Jimbo Jonny
15年12月22日在20:00

我更喜欢最后一个使用properties()函数的选项。但是,在扩展类时,事情会变得有些复杂,因此我建议如果在所有类中都使用properties()函数来声明您在类属性中声明的方法名,则该方法名将以类名作为前缀(即:MyClassProperties()为了避免意外覆盖子类中的函数调用,还请记住,任何对super()的调用都必须在类构造函数中首先声明。

–重块
16-09-26在23:33

#6 楼

那老式的方式呢?

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


在构造函数中,您仅提及那些必须计算的变量。
我喜欢此功能的原型继承-它可以帮助节省大量内存(以防万一有很多从未分配的var)。

评论


这不会节省内存,只会使性能比您在构造函数中声明它们时差得多。 codereview.stackexchange.com/a/28360/9258

– Esailija
2015年2月13日在22:34



同样,这也破坏了首先使用ES6类的目的。就目前而言,似乎很难像真正的oop类一样使用ES6类,这有点令人失望。

–科科多科
15年12月22日在14:45

@Kokodoko真正的哎呀-您的意思是,例如在Java中?我同意,我发现很多人生气的JS,因为他们尝试使用它像Java一样,他们习惯了,因为JS语法看起来相似,他们认为它的工作方式相同。但是从在内部,我们知道这是完全不同的。

–扎尔肯
15年12月23日在6:01

但是,这不仅与语言语法有关。一般而言,OOP哲学非常适合编程-尤其是在创建应用和游戏时。传统上,JS是在构建网页时实施的,这是完全不同的。由于网络也越来越重视应用程序,因此这两种方法需要以某种方式结合在一起。

–科科多科
15年12月23日在9:47

@Kokodoko仅仅因为它是一个不同的OOP并不意味着它不是OOP。原型是一种100%有效的OOP方法;没有人会称自己为“非OOP”,因为它使用了原型。不匹配另一个OOP范例就意味着:这是不同的。

–戴夫·牛顿
17年5月9日19:42

#7 楼

正如本杰明在回答中所说,TC39明确决定至少在ES2015中不包括此功能。但是,共识似乎是他们将在ES2016中添加它。

语法尚未确定,但是ES2016有一个初步建议,该提议允许您在类上声明静态属性。 。

由于通天塔的神奇之处,您可以立即使用它。根据这些说明启用类属性转换,您可以开始进行。以下是语法示例:

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


该提案处于非常早期的状态,因此,随着时间的流逝,请准备好调整语法。

评论


是的,那不是ES6。除非您知道它是什么,否则不要使用它。

–贝尔吉
2015年12月17日的1:59

#8 楼

[长线程,不确定它是否已作为选项列出...]。
仅用于常量的简单替代方法是在类外部定义const。
除非带有吸气剂,否则只能从模块本身进行访问。
这样,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);
    }
}


评论


如果您a)使用var而不是const 2)使用此类的多个实例会怎样?然后,将在类的每个实例中更改外部变量

–尼克·卡拉威
19年4月24日在20:49

失败点@nickcarraway,我的报价仅代表const,而不是标题为的问题。

–榛子吉尼斯
19年4月25日在19:32

#9 楼


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




评论


您已经完成了实例变量,而不是类变量。

– Tjad Clark
19年1月5日在9:19

#10 楼

您可以模仿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

评论


整个事情看起来很混乱。

– j4hangir
12月8日下午16:18

#11 楼

如果只是混乱的原因导致constructor出现问题,为什么不执行initialize变量的intializes方法。当构造函数充满不必要的东西时,这是正常的事情。即使在像C#这样的键入程序语言中,其常规约定也要添加Initialize方法来处理该问题。

#12 楼

我解决此问题的方法是另一种选择(如果您可以使用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(),
        });

    }
}


最终还是使用'fat'构造函数,但是至少将其全部放在一个类中并一次命中。

编辑#2:
我现在走了整整一圈,现在在构造函数中分配值,例如

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


为什么?实际上,很简单,使用上面的代码和一些JSdoc注释,PHPStorm能够对属性执行代码完成。一次命中分配所有var很好,但是无法编写完整的属性imo不值得(几乎肯定是很小的)性能好处。

评论


如果可以执行类语法,则还可以执行Object.assign。不需要jQuery。

–贝尔吉
17年5月10日在23:23

我看不到将MyClassFields构造为构造函数的任何意义-它没有任何方法。您能否详细说明为什么这样做(而不是说返回对象文字的简单工厂函数)?

–贝尔吉
17年5月10日在23:27

@Bergi-说实话,可能是习惯的力量,而不是其他任何事情。关于Object.assign的电话也很好-对此一无所知!

– Steve Childs
17年5月11日在7:32



#13 楼

好吧,您可以在构造函数中声明变量。

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

var foo = new Foo()

foo.method()


评论


您需要创建此类的实例才能使用此变量。静态函数无法访问该变量

– Aditya
18年8月30日在12:35

#14 楼

仍然不能像使用其他编程语言一样声明任何类。但是您可以创建尽可能多的类变量。但是问题是类对象的范围。因此,根据我的观点,ES6 Javascript中OOP编程的最佳方式:-

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


#15 楼

只需定义一个吸气剂即可。



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

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

var obj = new MyClass(); 




#16 楼

这是一些静态的静态组合,可以为我工作

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)...


评论


我建议将singleton_instance作为属性名称,以使每个人都知道您在这里做什么。

–贝尔吉
15年9月12日在11:35