目前在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。)

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


当前回答

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

其他回答

你可以模仿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();
    }
}

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

[长线程,不确定它是否已经列为一个选项…]。 仅用于常量的一个简单替代方法是在类外部定义const。 除非附带getter,否则只能从模块本身访问。 这样原型就不会被乱扔,你就能得到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);
    }
}

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

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

MyClass { 静态foo = 3; } console.log (MyClass.foo);

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

静态getter看起来非常接近:

MyClass { 静态获取CONST() { 返回3; } } MyClass。Const = 4;//财产不受影响 console.log (MyClass.CONST);