529

現在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たオブジェクトを渡します。すると、ハンドラはクラスにアタッチされます。何とか統合することも考えていました。)parameterWeakMaps

この状況に対処するには、どのようなアイデアが必要ですか?

4

17 に答える 17

542

2018 更新:

現在、ステージ 3 の提案があります。この回答を数か月で廃止することを楽しみにしています。

それまでの間、TypeScript または babel を使用している人は誰でも次の構文を使用できます。

varName = value

クラス宣言/式本体内で、変数を定義します。うまくいけば、数か月/数週間で更新を投稿できるようになります。

更新: Chrome 74 では、この構文が機能するようになりました。


ES6 での提案に関する ES wiki のメモ (最大最小クラス) は次のとおりです。

(意図的に) プロトタイプ データ プロパティ (メソッド以外) のクラス プロパティまたはインスタンス プロパティを直接宣言する方法はありません。

クラス プロパティとプロトタイプ データ プロパティは、宣言の外で作成する必要があります。

クラス定義で指定されたプロパティには、オブジェクト リテラルに表示されている場合と同じ属性が割り当てられます。

これは、あなたが求めているものが考慮され、明示的に反対されたことを意味します。

しかし、なぜ?

良い質問。TC39 の善良な人々は、クラス宣言でクラスの機能を宣言および定義することを望んでいます。そのメンバーではありません。ES6 クラス宣言は、そのユーザーのコントラクトを定義します。

クラス定義はプロトタイプメソッドを定義することを思い出してください。プロトタイプで変数を定義することは、通常、ユーザーが行うことではありません。もちろん、次を使用できます。

constructor(){
    this.foo = bar
}

あなたが提案したようなコンストラクターで。コンセンサスの概要も参照してください。

ES7以降

クラス宣言と式を介してより簡潔なインスタンス変数を許可する ES7 の新しい提案に取り組んでいます - https://esdiscuss.org/topic/es7-property-initializers

于 2014-04-10T11:20:03.470 に答える
137

ベンジャミンの答えに追加するだけです—クラス変数は可能ですが、それらを設定するために使用することはありませんprototype

真のクラス変数の場合、次のようなことをしたいと思うでしょう:

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

this.constructor.fooクラスメソッド内から、その変数は(または)としてアクセスできますMyClass.foo

これらのクラス プロパティは、通常、クラス インスタンスからはアクセスできません。つまりMyClass.foo、与えます'bar'が、new MyClass().fooですundefined

インスタンスからクラス変数にもアクセスしたい場合は、ゲッターを追加で定義する必要があります。

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

MyClass.foo = 'bar';

これは Traceur でしかテストしていませんが、標準の実装でも同じように機能すると思います。

JavaScript には実際にはクラスがありません。ES6 でも、クラスベースの言語ではなく、オブジェクトまたはプロトタイプベースの言語を見ています。いずれかfunction X () {}で、X.prototype.constructorを指しますXnew演算子が on で使用されると、Xを継承して新しいオブジェクトが作成されX.prototypeます。その新しいオブジェクト ( を含む) の未定義のプロパティは、そこから検索constructorれます。これは、オブジェクトとクラスのプロパティを生成することと考えることができます。

于 2014-07-30T11:51:31.870 に答える
31

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'
于 2016-02-15T14:41:38.547 に答える
19

あなたの問題はほとんどが文体的であるため(コンストラクターを一連の宣言で埋めたくない)、文体的にも解決できます。

私の見方では、多くのクラスベースの言語では、コンストラクターがクラス名自体にちなんで名付けられた関数になっています。スタイル的にはそれを使用して、スタイル的には意味のある ES6 クラスを作成できますが、コンストラクターで行われる典型的なアクションを、実行中のすべてのプロパティ宣言とグループ化することはありません。実際の JS コンストラクターを「宣言領域」として使用するだけで、それ以外の場合は「他のコンストラクターのもの」領域として扱う function という名前のクラスを作成し、真のコンストラクターの最後でそれを呼び出します。

"厳密に使用";

クラス MyClass
{
    // プロパティのみを宣言してから、this.ClassName() を呼び出します。ここから
    コンストラクタ(){
        this.prop1 = '何とか1';
        this.prop2 = 'なんとか2';
        this.prop3 = '何とか 3';
        this.MyClass();
    }

    // 他のあらゆる種類の「コンストラクター」のもので、もはや宣言でごちゃごちゃしていません
    私のクラス() {
        doWhatever();
    }
}

新しいインスタンスが構築されると、両方が呼び出されます。

宣言と実行したい他のコンストラクターアクションを分離する2つのコンストラクターを持つようなもので、スタイル的にも、それが起こっていることを理解するのがそれほど難しくありません。

多くの宣言や、インスタンス化で発生する必要がある多くのアクションを処理し、2 つのアイデアを互いに区別したい場合に使用するのに適したスタイルだと思います。


init(): 「初期化」の典型的な慣用的な考え方 ( orinitialize()メソッドなど)は意図的に使用しません。構築と初期化の考え方には、ある種の推定上の違いがあります。コンストラクターを使用すると、インスタンス化の一部として自動的に呼び出されることを人々は知っています。メソッドを見ると、init多くの人が の形式に沿って何かを行う必要があることを一目見ずに想定しvar mc = MyClass(); mc.init();ます。クラスのユーザーの初期化プロセスを追加しようとしているのではなくクラス自体の構築プロセスに追加しようとしています。

一部の人々は一瞬二刀流をするかもしれませんが、それは実際にはちょっとしたポイントです: 意図が建設の一部であることを彼らに伝えます.どうやって ES6 コンストラクターが機能するか」を見て、実際のコンストラクターをもう一度見て、「ああ、彼らは一番下でそれを呼び出しているようですね」間違った使い方をしたり、外部から初期化しようとしたり、ジャンクしたりする人。これは、私が提案するパターンに対して非常に意図的なものです。


そのパターンに従いたくない場合は、正反対の方法も有効です。最初に宣言を別の関数にファームアウトします。「プロパティ」または「publicProperties」などの名前を付けてください。次に、残りのものを通常のコンストラクターに入れます。

"厳密に使用";

クラス MyClass
{
    プロパティ() {
        this.prop1 = '何とか1';
        this.prop2 = 'なんとか2';
        this.prop3 = '何とか 3';
    }

    コンストラクター() {
        this.properties();
        doWhatever();
    }
}

この 2 番目のメソッドはよりきれいに見えるかもしれませんが、propertiesこのメソッドを使用するクラスが別のクラスを拡張するときにオーバーライドされるという固有の問題もあります。propertiesそれを避けるには、より一意の名前を付ける必要があります。私の最初のメソッドには、コンストラクターの偽の半分がクラスにちなんで一意に名前が付けられているため、この問題はありません。

于 2015-12-22T07:21:05.397 に答える
14

Benjamin が回答で述べたように、TC39 は、少なくとも ES2015 ではこの機能を含めないことを明示的に決定しました。ただし、コンセンサスは、ES2016 で追加することであると思われます。

構文はまだ決定されていませんが、クラスで静的プロパティを宣言できるようにする ES2016の暫定的な提案があります。

バベルの魔法のおかげで、今日から使えます。これらの手順に従ってクラス プロパティの変換を有効にすると、準備完了です。構文の例を次に示します。

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

この提案は非常に初期の状態であるため、時間が経つにつれて構文を微調整する準備をしてください。

于 2015-12-16T21:36:41.983 に答える
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に載せました

于 2017-05-29T02:14:20.403 に答える