ES6クラスで「パブリック静的フィールド」を作成するにはどうすればよいですか?


86

Javascriptクラスを作成していますが、Javaのようにパブリック静的フィールドが必要です。これは関連するコードです:

export default class Agent {
    CIRCLE: 1,
    SQUARE: 2,
    ...

これは私が得るエラーです:

line 2, col 11, Class properties must be methods. Expected '(' but instead saw ':'.

ES6モジュールはこれを許可していないようです。目的の動作を取得する方法はありますか、それともゲッターを作成する必要がありますか?


どのECMAScript6エンジン実装を使用していますか?
ダイ

回答:


136

アクセサーと「static」キーワードを使用して「publicstaticfield」を作成します。

class Agent {
    static get CIRCLE() {
      return 1;
    }
    static get SQUARE() {
      return 2;
    }
}

Agent.CIRCLE; // 1

仕様を見ると、14.5 —クラス定義—疑わしい関連性があることがわかります:)

ClassElement [Yield]:
  MethodDefinition [?Yield]
  static MethodDefinition [?Yield];

したがって、そこから14.5.14 —ランタイムセマンティクス:ClassDefinitionEvaluation —に従って、実際にそのように見えるかどうかを再確認できます。具体的には、ステップ20:

  1. 各ClassElementmについて、メソッドから順番に
    1. 場合メートルのIsStaticは偽である場合、
      1. ステータスを、引数protoおよびfalseを使用してmに対してPropertyDefinitionEvaluationを実行した結果とします。
    2. そうしないと、
      1. ステータスを、引数Fおよびfalseを指定してmに対してPropertyDefinitionEvaluationを実行した結果とします。
    3. ステータスが突然の完了の場合、
      1. 実行中の実行コンテキストのLexicalEnvironmentをlexに設定します。
      2. ステータスを返します。

IsStaticは14.5.9の前半で定義されています

ClassElement:static MethodDefinitiontrueを
返します。

したがってPropertyMethodDefinition、引数として「F」(コンストラクター、関数オブジェクト)を使用して呼び出され、そのオブジェクトにアクセサーメソッドが作成されます

これは、少なくともIETP(技術プレビュー)、および6to5コンパイラとTraceurコンパイラですでに機能します。


探している他の人にとって、静的アクセサプロパティはまだノードでサポートされていません。:-/ kangax.github.io/compat-table/es6/…–
David Hernandez

1
少なくともNode.js6.x +以降、これはサポートされています。
NuSkooler 2017

フローを使用している場合はunsafe.enable_getters_and_setters=true、.flowconfigの下に行を追加する必要があることに注意してください[options](これは煩わしいです)。
クリスティーナ

これは私には機能しません私は `` `を取得しています未処理の拒否TypeError:クラスCollectionsのプロパティdataHashKeyを設定できません{api_1 | static get dataHashKey(){api_1 | 'コレクション'を返します。api_1 | } `` `
Pavan 2018年

54

この問題を解決することを目的とした、DanielEhrenbergとJeffMorrisonによる「StaticClassFeatures」と呼ばれるステージ3ECMAScriptの提案があります。ステージ3の「クラスフィールド」の提案に加えて、将来のコードは次のようになります。

class MyClass {
    static myStaticProp = 42;
    myProp = 42;
    myProp2 = this.myProp;
    myBoundFunc = () => { console.log(this.myProp); };

    constructor() {
        console.log(MyClass.myStaticProp); // Prints '42'
        console.log(this.myProp); // Prints '42'
        this.myBoundFunc(); // Prints '42'
    }
}

上記は次と同等です。

class MyClass {
    constructor() {
        this.myProp = 42;
        this.myProp2 = this.myProp;
        this.myBoundFunc = () => { console.log(this.myProp); };

        console.log(MyClass.myStaticProp); // Prints '42'
        console.log(this.myProp); // Prints '42'
        this.myBoundFunc(); // Prints '42'
    }
}
MyClass.myStaticProp = 42;

Babel @ babel / plugin-proposal-class-propertiesstage-3プリセットに含まれています)を介したクラスフィールドのトランスパイルをサポートしているため、JavaScriptランタイムがサポートしていない場合でもこの機能を使用できます。


ゲッターを宣言する@kangaxのソリューションと比較すると、このソリューションは、関数を呼び出す代わりにプロパティに直接アクセスされるため、パフォーマンスも向上します。

この提案が受け入れられれば、JavaやC♯などの従来のオブジェクト指向言語に似た方法でJavaScriptコードを記述できるようになります。


編集:統一されたクラスフィールドの提案は現在ステージ3にあります。Babelv7.xパッケージに更新します。

編集(2020年2月):静的クラス機能は別の提案に分割されました。ありがとう@ GOTO0!


関連する提案は実際にはこれだと思います(静的クラス機能)。
GOTO

29

ECMAScript 6の現在のドラフト(2015年2月現在)では、すべてのクラスプロパティは値ではなくメソッドである必要があります(ECMAScriptの「プロパティ」は、フィールド値がFunctionオブジェクトである必要があり、aNumberObject)などの他の値。

従来のECMAScriptコンストラクタープロパティ指定子を使用して、これらを引き続き指定できます。

 class Agent {
 }
 Agent.CIRCLE = 1;
 Agent.SQUARE = 2;
 ...

11
ES6class構文は、とにかく従来のJSコンストラクター関数とプロトタイプの単なる構文糖衣であることに注意してください。
マットブラウン

これらのプロパティをコンストラクターではなくプロトタイプに配置して、インスタンスからのプロパティ参照を介して表示できるようにしたいと思います。
とがった2015

@Pointy OPが参照用の定数を格納しようとしていると推測しました(ほとんどC#/。NETのようにenum)。
ダイ

2
@MattBrowneはい。ただし、明確にするために、class構文にも微妙な違いがあります。たとえば、で宣言されたメソッドClass.prototype.method = function () {};列挙可能です(for-inループで表示されます)が、classメソッドは列挙可能ではありません。
Timothy Gu 2017年

4

静的変数を最大限に活用するために、私はこのアプローチに従いました。具体的には、これを使用して、プライベート変数を使用するか、パブリックゲッターのみを使用するか、ゲッターまたはセッターの両方を使用できます。最後のケースでは、上記の解決策の1つと同じです。

var Url = (() => {
    let _staticMember = [];
    return class {
        static getQueries(hash = document.location.hash) {
            return hash;
        }

        static get staticMember(){
            return _staticMember;
        }
    };
})();

Usages:
console.log(Url.staticMember); // [];
Url.staticMember.push('it works');
console.log(Url.staticMember); // ['it works'];

Urlを拡張する別のクラスを作成でき、それは機能しました。

ES6コードをES5に変換するためにbabelを使用しました


1
「フルアドバンテージ」とは何ですか?class Url { static getQueries… }; Url.staticMember = [];もっと簡単ではなかっただろうか?
ベルギ2015

これらの===比較は両方ともfalse、btw
Bergi

「フルアドバンテージ」とは、上記の方法で、必要に応じて_staticMemberをプライベートとして保持できることを意味します。
SM Adnan 2015

-1

@kangaxの回答は、従来のOOP言語の静的な動作全体を模倣しているわけではありません。これは、次のようなインスタンスでは静的プロパティにアクセスできないためです。 const agent = new Agent; agent.CIRCLE; // Undefined

OOPのように静的プロパティにアクセスしたい場合は、次のようにします。

class NewApp {
  get MULTIPLE_VERSIONS_SUPPORTED() {
    return this.constructor.MULTIPLE_VERSIONS_SUPPORTED; // Late binding for inheritance
  }
}

NewApp.MULTIPLE_VERSIONS_SUPPORTED = true;

次のようにコードをテストします。

class NewApp {
  get MULTIPLE_VERSIONS_SUPPORTED() {
    console.log('this.constructor.name:', this.constructor.name); // late binding
    return this.constructor.MULTIPLE_VERSIONS_SUPPORTED;
  }
}

// Static property can be accessed by class
NewApp.MULTIPLE_VERSIONS_SUPPORTED = true;

const newApp = new NewApp;

// Static property can be accessed by it's instances
console.log('newApp.MULTIPLE_VERSIONS_SUPPORTED:', newApp.MULTIPLE_VERSIONS_SUPPORTED); // true

// Inheritance
class StandardApp extends NewApp {}

// Static property can be inherited
console.log('StandardApp.MULTIPLE_VERSIONS_SUPPORTED:', StandardApp.MULTIPLE_VERSIONS_SUPPORTED); // true

// Static property can be overwritten
StandardApp.MULTIPLE_VERSIONS_SUPPORTED = false;

const std = new StandardApp;

console.log('std.MULTIPLE_VERSIONS_SUPPORTED:', std.MULTIPLE_VERSIONS_SUPPORTED); // false


1
staticインスタンスによるフィールドへのアクセスはかなり珍しいと思いませんか?Javaなどの一部の言語では、そのようなことを行うと、IDEは実際に警告/ヒントを発行します。
isac 2018年

@Isacはい、その通りです。インスタンスによるアクセスはお勧めできません。私の答えもそうです。ソリューションのちょうど別の視点。😀
legend80s
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.