web-dev-qa-db-ja.com

ES6クラス変数の代替

現在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;
    }
}

残念ながら、クラスにはメソッドしか含めることができないため、上記は機能しません。

constructorthis.myVar = trueを入れることができることを私は理解しています…しかし私は私のコンストラクタを「ジャンク」したくありません。

私はこの問題に対処するために多くの方法を考えていましたが、まだ良いものを見つけていません。 (例:ClassConfigハンドラーを作成し、クラスとは別に宣言されたparameterオブジェクトを渡します。そうするとハンドラーはクラスにアタッチします。統合するためにWeakMapsも考えました。)

このような状況に対処するためにどのようなアイデアが必要ですか。

454
wintercounter

2018年の更新:

現在ステージ3の提案があります - 私はこの回答が数ヶ月で時代遅れになるのを楽しみにしています。

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

varName = value

クラス宣言/式本体の中で、それは変数を定義します。うまくいけば、数ヶ月/数週間のうちに私はアップデートを投稿できるようになるでしょう。

アップデート:Chrome 74はこのシンタックスを有効にして出荷されます。


ES6での提案に関するESウィキのメモ( 最大最小クラス ))

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

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

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

これはつまり、 あなたが求めているものが考慮され、明示的に反対に決められたということです。

しかし、なぜ?

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

覚えておいて、クラス定義はprototypeメソッドを定義します - プロトタイプで変数を定義することは一般的にあなたがすることではありません。もちろん、次のことができます。

constructor(){
    this.foo = bar
}

あなたが提案したようなコンストラクタで。 コンセンサスの要約 も参照).

ES7以降

ES7の新しい提案が進められています。それはクラス宣言と式を通してより簡潔なインスタンス変数を許可する - https://esdiscuss.org/topic/es7-property-initializers

478

Benjaminの答えに加えて、クラス変数は可能ですが、それらを設定するのにprototypeを使用することはありません。

真のクラス変数の場合は、次のようにします。

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

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

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

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

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

MyClass.foo = 'bar';

私はTraceurでこれをテストしただけですが、標準実装でも同じように動作すると思います。

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

118
lyschoening

あなたの例では:

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

しかし、static get MY_CONST() {return ['string'];}アラート出力が 文字列のようにMY_CONSTが参照型の場合、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
24
Oleg Mazko

Babel ESNextでクラス変数をサポートします。 example を確認してください。

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'
23
Kosmetika

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

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

[厳密に使用];厳密に宣言するMyClass 
 {
 //プロパティを宣言するだけでthis.ClassName();を呼び出すことができます。ここから
 constructor(){
 this.prop1 = 'blah 1'; 
 this.prop2 = 'blah 2'; 
 this.prop3 = 'blah 3 '; 
 this.MyClass(); 
} 
 
 //その他の「コンストラクタ」のあらゆる種類のもの。 ] MyClass(){
 doWhatever(); 
} 
} 

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

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

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


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

ちょっとしたダブルテイクをする人もいるかもしれませんが、それは実際にはちょっとしたことです。意図が建設の一部であることを彼らに伝えます。 ES6のコンストラクタがどのように機能するのか、そして実際のコンストラクタを見ていくためには、「ああ、それを一番下のほうで呼んでいます」と言っています。人々はそれを間違って使っていて、外側とジャンクからそれを初期化しようとしています。これは私が提案するパターンに対して非常に意図的なものです。


そのパターンに従いたくない人にとっては、正反対のこともうまくいきます。最初に宣言を別の関数に渡します。多分それを "properties"か "publicProperties"か何かと名付けてください。それから残りのものを通常のコンストラクタに入れます。

[厳密] [厳密]を使用してください[厳密] [] [] MyClass [[]] properties(){[。] this.prop1 = 'blah 1' ; 
 this.prop2 = 'blah 2'; 
 this.prop3 = 'blah 3'; 
} 
 
 constructor(){[ this.properties(); 
何でも(); 
} 
} 

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

16
Jimbo Jonny

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

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

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

14
zarkone

Benjaminが彼の答えで言ったように、TC39は少なくともES2015のためにこの機能を含まないことを明示的に決定しました。ただし、ES2016で追加される予定であるとの見方が一致しています。

構文はまだ決まっていませんが、ES2016用に 予備提案 を使用すると、クラスで静的プロパティを宣言できます。

Babelの魔法のおかげで、あなたは今日これを使うことができます。 これらの指示 に従ってクラスプロパティ変換を有効にしてください。これが構文の例です。

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

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

13
BonsaiOak

[長いスレッド、すでにオプションとしてリストされているかどうかわからない...]。
単純な alternative conntsantsのみ は、クラス外でconstを定義しているでしょう。ゲッターを伴わない限り、これはモジュール自身からのみアクセス可能です。
この方法でprototypeが散らばることはなく、あなたは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);
    }
}
7

あなたは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 に乗せて

5
Ruslan

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

私がこれを解決する方法は、もう1つの選択肢です(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(),
        });

    }
}

それでもやはり 'fat'コンストラクタになりますが、少なくともそのすべてが1つのクラスにまとめられ、1回のヒットで割り当てられます。

編集#2: 私は今一周して、コンストラクタに値を代入しています。

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

どうして?非常に単純で、上記に加えていくつかのJSdocコメントを使用して、PHPStormはプロパティのコード補完を実行することができました。 1回のヒットですべての変数を割り当てるのはいいことですが、プロパティをコードで完成させることができないこと、つまり、(ほとんど間違いなく)パフォーマンス上の利点はありません。

0
Steve Childs

さて、あなたはコンストラクタの中で変数を宣言することができます。

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

var foo = new Foo()

foo.method()
0
Osama Xäwãñz