ES6クラス変数の代替


493

現在、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ハンドラーを作成parameterし、クラスとは別に宣言されているオブジェクトを渡します。次に、ハンドラーがクラスにアタッチされます。WeakMapsどういうわけか、統合することも考えていました。)

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


1
あなたの主な問題は、this.member = memberあなたのコンストラクタで20〜30のパラメータで繰り返しがあるということですか?
ΘεόφιλοςΜουρατίδης

1
public variable2 = trueクラスだけで使えないの?これはプロトタイプでそれを定義します。

14
@ΘεόφιλοςΜουρατίδης:はい、また、コンストラクターを変数宣言ではなく初期化手順に使用したいと思います。
Wintercounter 14

@derylius:これが主な問題で、そのような機能はありません。ES6ドラフトでは、パブリック/プライベートの使用もまだ決定されていません。試してみましょう:es6fiddle.net
wintercounter

最新によると、それはこの機能を持っています:wiki.ecmascript.org/doku.php?id

回答:


515

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


4
@wintercounterから取得する重要なことは、プロパティの定義を許可すると、各インスタンスではなく、メソッドのようにプロトタイプでプロパティが定義されることです。最大の最小限のクラスは、依然としてそのコアとなるプロトタイプの継承にあります。ケースで本当にやりたいことは、共有構造と各インスタンスのメンバーの割り当てです。これは単にES6のクラスが目指すものではなく、機能を共有することです。だから、はい、構造を共有するには、古い構文に固執する必要があります。ES7までは少なくとも:)
Benjamin Gruenbaum 2014

5
staticプロパティに言及したいと思うかもしれません
ベルギ

8
おっと、おなら。staticメソッドでのみ機能することを忘れていました。
ベルギ

638
たぶん、TC39の優秀な人は、プログラミングの世界の他の人が「クラス」という名前から期待しているように動作させたくない場合は、この概念を「クラス」以外の名前にする必要があります。
Alex

7
「クラスフィールドと静的プロパティ」の提案も参照してください(すでにBabelに実装されています:github.com/jeffmo/es-class-fields-and-static-properties
Matt Browne

127

Benjaminの答えに追加するだけです。クラス変数は可能ですが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ポイントしXます。でnew演算子を使用するXと、新しいオブジェクトが継承して作成されX.prototypeます。どれ未定義(を含むその新しいオブジェクトのプロパティがconstructor)そこからルックアップされています。これは、オブジェクトとクラスのプロパティを生成するものと考えることができます。


29

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'

1
次のように、 "#"を付加してbarプライベートクラス変数にすることができます。# bar = 2;
ロニーベスト

26

あなたの例では:

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ような参照タイプstatic get MY_CONST() {return ['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

5
deleteパフォーマンス上の理由で単独の場合は、オペレーターを避けてください。ここで実際に必要なのはObject.definePropertyです。
Bergi、2015年

@shinzou同​​じことを考えていた。ただし、必ずしもOPのせいではありません。実世界の関係を反映する方法でデータを表現するための適切なメカニズムが欠けているのは、実際には言語です。
user1974458

17

あなたの問題はほとんどが文体的であるので(宣言の束でコンストラクタをいっぱいにしたくない)、それはスタイル的にも解決することができます。

私がそれを見ると、多くのクラスベースの言語では、コンストラクターはクラス名自体にちなんで名付けられた関数です。文体的には、それを使用して、文体的にはまだ理にかなっているが、コンストラクターで行われている一般的なアクションを、実行しているすべてのプロパティ宣言でグループ化しないES6クラスを作成できます。単に実際のJSコンストラクターを「宣言領域」として使用し、次に「その他のコンストラクタースタッフ」領域として扱う関数という名前のクラスを作成し、真のコンストラクターの最後で呼び出します。

「厳格に使用」;

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

    //他のあらゆる種類の「コンストラクタ」のもの。宣言でごちゃごちゃにならない
    私のクラス() {
        doWhatever();
    }
}

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

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

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


:「初期化」の一般的な慣用的なアイデア(init()またはinitialize()メソッドなど)は、意図的に使用しないでください。構築と初期化の考えには、ある種の推定される違いがあります。コンストラクターを操作すると、インスタンス化の一部として自動的に呼び出されることがわかります。init多くの人が一目見ずに、の形式に沿って何かをする必要があると思い込むメソッドを見ると、var mc = MyClass(); mc.init();それが通常の初期化方法です。私はクラスのユーザーのための初期化プロセスを追加しようとしていないよ、私が追加しようとしているクラス自体の建設プロセス。

一部の人々は一瞬ダブルテイクをするかもしれませんが、それは実際には少し重要です。それは、意図が建設の一部であることを彼らに伝えます。 ES6コンストラクターのしくみ」と実際のコンストラクターをもう一度見て、「ああ、彼らはそれを一番下に呼びます」と言います。これは、その意図を伝えない(または誤って伝える)ことよりもはるかに優れており、おそらく多くのそれを間違って使用し、外部やジャンクから初期化しようとしています。それは私が提案するパターンに非常に意図的です。


そのパターンにしたくない人のために、正反対も機能することができます。最初に宣言を別の関数にファームします。「プロパティ」や「publicProperties」などの名前を付けます。次に、残りのものを通常のコンストラクターに入れます。

「厳格に使用」;

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

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

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


1
クラス自体にちなんで名付けられたプロトタイプメソッドを使用しないでください。これはJSでは慣用的ではありません。別の言語のように見せないでください。このアプローチを本当に使用したい場合、メソッドの正規名はinitです。
Bergi

1
@Bergi- initは頻繁に使用されるパターンであり、外部ユーザーが初期化するときに、クラスの外部から呼び出されることを意図していますvar b = new Thing(); b.init();。これは100%スタイルの選択であり、他の言語にあるパターンを利用して自動的に呼び出される2番目の関数であることを伝えたいと思います。誰かがこれを見てMyClass、外部からメソッドを呼び出す必要があると想定する可能性ははるかに低く、インテントは構築で機能する2番目のメソッド(つまり、インスタンス化時に単独で呼び出される)であることに気づくでしょう。
神保ジョニー2015

ええと、コンストラクターを見ても、メソッド名からではないことに気付くかもしれませんMyClass
Bergi

1
@Bergi-別の言語からパターンを取得し、それを技術的に何が起こっているのかではない方法でJSに適用することは、完全に前例がないわけではありません。$myvarjQueryオブジェクトを保持することを目的とした変数を参照する標準的な方法が、多くのPHPプログラマーが慣れている変数の先頭に$を置くパターンに都合よく似ていないことに誰も気付いていないことはわかりません。「ええ、それはまったく同じものではありません...しかし、見てください...一部の言語では変数が行われる方法であるため、変数です」助けます。
神保ジョニー2015

1
私はあなたの最後のproperties()機能を好む。ただし、クラスを拡張する場合は少し複雑になるのでproperties()、すべてのクラスで関数を使用してクラスプロパティを宣言する場合は、メソッド名の前にクラス名を付けることをお勧めします(IE:MyClassProperties()誤ってオーバーライドしないようにするため)サブクラス内の関数呼び出し。また、への呼び出しはすべてsuper()クラスコンストラクターで最初に宣言する必要があることに注意してください
TheDarkIn1978

14

オールドスクールの方法はどうですか?

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

コンストラクターでは、計算する必要のある変数のみに言及します。私はこの機能のプロトタイプ継承が好きです-多くのメモリを節約するのに役立ちます(割り当てられていない変数がたくさんある場合)。


4
これはメモリを節約せず、コンストラクターで宣言した場合よりもパフォーマンスを大幅に低下させるだけです。codereview.stackexchange.com/a/28360/9258
エサイリア

2
また、これはもともとES6クラスを使用する目的に反します。現状では、実際のoopクラスのようなES6クラスを使用することはほとんど不可能のようです。これは少しがっかりです...
Kokodoko

4
@Kokodoko real oop-つまり、Javaのようにですか?私は同意します。JS構文は似ているため、慣れているようにJavaのように使用しようとするため、JSに腹を立てている人が多いことに気付きました。私たちが知っているように、内部はかなり異なります。
zarkone

2
ただし、これは言語構文だけではありません。OOPの哲学は、特にアプリやゲームを作成する場合に、プログラミング全般に非常に適しています。JSは従来、まったく異なるWebページの構築に実装されてきました。どういうわけか、ウェブはアプリについてもますます重要になってきているので、これら2つのアプローチを組み合わせる必要があります。
Kokodoko

1
@ココドコOOPが違うからといって、それ OOPではないという意味ではありません。プロトタイプは100%有効なOOPアプローチです。プロトタイプを使用しているため、Selfを「非OOP」と呼ぶ人はいません。別のOOPパラダイムに一致しないということは、それだけを意味します。
デイブニュートン

13

ベンジャミンが彼の回答で述べたように、TC39は少なくともES2015にはこの機能を含めないことを明確に決定しました。ただし、コンセンサスは、ES2016で追加されることです。

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

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

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

この提案はごく初期の段階なので、時間が経つにつれ構文を微調整できるように準備しておいてください。


はい、それはES6ではありません。それが何であるかを知らない限り、それを使うべきではありません。
Bergi、2015

10

[長いスレッド、すでにオプションとしてリストされているかどうかは不明です...]。定数のみの
単純な代替案は、クラスの外で定数を定義することです。これは、ゲッターを伴わない限り、モジュール自体からのみアクセスできます。 この方法はポイ捨てされず、を取得します。
prototypeconst

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

2)のvar代わりにa)を使用した場合、どうなりますconstか?このクラスの複数のインスタンスを使用しますか?次に、外部変数はクラスのインスタンスごとに変更されます
ニックキャラウェイ

1
失敗点@nickcarraway、私の申し出はタイトル付きの質問ではなく、constのみを表しています。
ヘルツェルギネス

6

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


3
クラス変数ではなく、インスタンス変数を完成させました。
Tjad Clark

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に置いた


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

    }
}

あなたはまだ「脂肪」コンストラクターで終わりますが、少なくともすべてが1つのクラスにあり、1つのヒットで割り当てられます。

編集#2: 私は今、完全に一周し、今コンストラクタに値を割り当てています、例えば

class SavedSearch  {
    constructor() {
        this.id = '';
        this.type = '';
        this.title = '';
        this.createdAt = new Date();
    }
}

どうして?本当に簡単です。上記といくつかのJSdocコメントを使用して、PHPStormはプロパティでコード補完を実行できました。1回のヒットですべての変数を割り当てるのは良かったですが、プロパティimoをコード化できないので、(ほとんど確実に)パフォーマンス上のメリットはありません。


2
class構文を実行できる場合は、も実行できますObject.assign。jQueryは必要ありません。
ベルギ

MyClassFieldsコンストラクタを作成する意味が見当たらない-メソッドがない。これを行った理由を詳しく説明できますか(オブジェクトリテラルを返す単純なファクトリ関数の代わりに)?
ベルギ

@Bergi-正直に言うと、おそらく何よりも習慣の力。Object.assignについてもいい呼び出しです-それについて知りませんでした!
スティーブチャイルズ

0

まあ、コンストラクタ内で変数を宣言できます。

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

var foo = new Foo()

foo.method()

1
この変数を使用するには、このクラスのインスタンスを作成する必要があります。静的関数はその変数にアクセスできない
Aditya

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
   }
}

-1

これは静的なものの少しハックっぽい組み合わせであり、私のために作品を手に入れます

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

7
singleton_instance皆さんがここで何をしているのか誰もが理解できるように、プロパティー名としてお勧めします。
ベルギ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.