ES6类可变替代品

时间:2014-03-20 09:45:42

标签: javascript class ecmascript-6

目前在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 ...但我不想要“垃圾”#39;我的构造函数,特别是当我有20-30 + params为更大的类。

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

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

17 个答案:

答案 0 :(得分:482)

2018更新:

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

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

varName = value

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

更新:Chrome 74现在提供此语法。


ES维基中关于ES6中提案的注释(maximally minimal classes)注意:

  

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

     

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

     

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

这意味着考虑了您所要求的内容,并明确决定反对。

但......为什么?

好问题。 TC39的优秀人员希望类声明声明和定义类的功能。不是它的成员。 ES6类声明定义了其用户的合同。

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

constructor(){
    this.foo = bar
}

在你建议的构造函数中。另请参阅the summary of the consensus

ES7及以上

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

答案 1 :(得分:119)

只是添加到Benjamin的答案 - 类变量是可能的,但你不会使用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 doesn't really have classes。即使使用ES6,我们也在寻找基于对象或原型的语言而不是基于类的语言。在任何function X () {}X.prototype.constructor点回X。 在X上使用new运算符时,会创建一个继承X.prototype的新对象。从那里查找该新对象(包括constructor)中的任何未定义属性。我们可以将其视为生成对象和类属性。

答案 2 :(得分:24)

在你的例子中:

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_CONSTstatic get MY_CONST() {return ['string'];}之类的引用类型,则警报输出为 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

答案 3 :(得分:23)

Babel支持ESNext中的类变量,请检查此example

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'

答案 4 :(得分:16)

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

我查看它的方式,许多基于类的语言使构造函数成为以类名本身命名的函数。在风格上我们可以使用它来创建一个风格上仍然有意义的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构造函数的工作方式”并且花一点时间看看实际的构造函数“哦,他们在底部调用它,我看到了”,这比不传达那个意图(或者错误地传达它)并且可能得到的要好得多很多人使用它错了,试图从外面和垃圾中初始化它。这对我建议的模式非常有意。

对于那些不想遵循这种模式的人来说,恰恰相反也可以。在最开始将声明存储到另一个函数中。可能将其命名为“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提供更多唯一名称以避免这种情况。我的第一个方法没有这个问题,因为构造函数的假半部分是在类之后唯一命名的。

答案 5 :(得分:14)

oldschool方式怎么样?

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

在构造函数中,您只提到那些必须计算的变量。 我喜欢这个功能的原型继承 - 它可以帮助节省大量内存(如果有很多未分配的变量)。

答案 6 :(得分:13)

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

语法尚未确定,但ES2016的preliminary proposal允许您在类上声明静态属性。

感谢巴贝尔的魔力,你今天可以使用它。根据{{​​3}}启用类属性转换,您就可以了。以下是语法示例:

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

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

答案 7 :(得分:7)

[长线程,不确定它是否已作为选项列出......] 仅仅限contsants 的简单替代,将定义类外的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);
    }
}

答案 8 :(得分:5)

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

答案 9 :(得分:3)

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

答案 10 :(得分:1)

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

答案 11 :(得分:0)

只需定义一个getter

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

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

var obj = new MyClass();

答案 12 :(得分:0)

仍然不能像使用其他编程语言一样声明任何类。但是您可以创建尽可能多的类变量。但是问题是类对象的范围。因此,根据我的观点,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
   }
}

答案 13 :(得分:0)

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

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

var foo = new Foo()

foo.method()

答案 14 :(得分:0)

我解决这个问题的方法是另一种选择(如果你有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能够在属性上执行代码完成。在一次点击中分配所有变量很好,但是无法编码完成属性imo,不值得(几乎肯定是微不足道的)性能优势。

答案 15 :(得分:-1)

你能通过使用强大的文字和一个包含在更大的闭包中的一小块模板逻辑库来避免整个问题吗?

暂时忽略关闭

const myDynamicInputs=(items)=>\backtick -${ items.map((item, I, array)=>'${do tons of junk}').join('')}';

http://codepen.io/jfrazz/pen/BQJPBZ/

这是我可以从存储库中提供的最简单的示例,前400行是数据库+一些基本的实用程序功能。加上一些实用常量。

在我们正在变成数据uri的锅炉板 - 由app用户下载 - 我们有阵列模板,必须被提升和重新部署,但可以组成任何来自输入,下拉,或52页的问题和数据。

这是第二个例子:吃一个对象,得到各种类型的输入,都使用const作为库的基本变量,正在构建。

http://codepen.io/jfrazz/pen/rWprVR/

不完全是你提出的问题,但清楚地表明常数可以非常动态。

答案 16 :(得分:-1)

这是静态的一些hackish组合,并为我工作

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