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

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


当前回答

ES7类成员语法:

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

车{ 轮子= 4; 重量= 100; } const car = new car (); console.log(车。轮子,car.weight);

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

车{ 构造函数(){ 这一点。轮子= 4; 这一点。重量= 100; } } const car = new car (); console.log(车。轮子,car.weight);

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

好处:一个对象工厂:

函数generateCar(车轮,重量){ 车{ 构造函数(){} 轮子=轮子; 重量=重量; } 返回新车(); } const car1 = generateCar(4,50); const car2 = generateCar(6,100); console.log (car1。轮子,car1.weight); console.log (car2。轮子,car2.weight);

其他回答

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

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

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

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

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

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的新对象。新对象中任何未定义的属性(包括构造函数)都将从那里查找。我们可以把这看作是生成对象和类属性。

[长线程,不确定它是否已经列为一个选项…]。 仅用于常量的一个简单替代方法是在类外部定义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);
    }
}

我解决这个问题的方法,这是另一个选择(如果你有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就能够在属性上执行代码补全。在一次命中分配所有的变量是很好的,但无法编码完成属性,在我看来,不值得(几乎肯定是微乎其微的)性能收益。

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