目前在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;
    }
}

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

我知道我能做到。myVar = true在构造函数…但我不想'垃圾'我的构造函数,特别是当我有20-30+参数为一个更大的类。

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

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


当前回答

在你的例子中:

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是引用类型,如静态get MY_CONST() {return ['string'];}警报输出是字符串,false。在这种情况下,删除操作符可以做到:

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

其他回答

那老派的方法呢?

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

在构造函数中,你只提到那些需要计算的变量。 我喜欢这个特性的原型继承——它可以帮助节省大量内存(以防有很多从未分配的变量)。

只需定义一个getter。

MyClass类 { get MY_CONST(){返回'string';} 构造函数() { console.log ("MyClass MY_CONST:", this.MY_CONST); } } var obj = new MyClass();

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

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

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

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

这些类属性通常不能被类实例访问。即MyClass。foo给出'bar'但新的MyClass()。Foo没有定义

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

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

MyClass.foo = 'bar';

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

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

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

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

The way I view it, many class based languages have the constructor be a function named after the class name itself. Stylistically we could use that that to make an ES6 class that stylistically still makes sense but does not group the typical actions taking place in the constructor with all the property declarations we're doing. We simply use the actual JS constructor as the "declaration area", then make a class named function that we otherwise treat as the "other constructor stuff" area, calling it at the end of the true constructor.

"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();
    }
}

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

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

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


NOTE: I very purposefully do not use the typical idiomatic ideas of "initializing" (like an init() or initialize() method) because those are often used differently. There is a sort of presumed difference between the idea of constructing and initializing. Working with constructors people know that they're called automatically as part of instantiation. Seeing an init method many people are going to assume without a second glance that they need to be doing something along the form of var mc = MyClass(); mc.init();, because that's how you typically initialize. I'm not trying to add an initialization process for the user of the class, I'm trying to add to the construction process of the class itself.

While some people may do a double-take for a moment, that's actually the bit of the point: it communicates to them that the intent is part of construction, even if that makes them do a bit of a double take and go "that's not how ES6 constructors work" and take a second looking at the actual constructor to go "oh, they call it at the bottom, I see", that's far better than NOT communicating that intent (or incorrectly communicating it) and probably getting a lot of people using it wrong, trying to initialize it from the outside and junk. That's very much intentional to the pattern I suggest.


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

"use strict";

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

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

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