web-dev-qa-db-fra.com

Comment créer un "champ statique public" dans une classe ES6?

Je fais une classe Javascript et j'aimerais avoir un champ public statique comme en Java. C'est le code pertinent:

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

C'est l'erreur que j'obtiens:

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

Il semble que les modules ES6 ne le permettent pas. Existe-t-il un moyen d'obtenir le comportement souhaité ou dois-je écrire un getter?

78
acbabis

Vous créez un "champ statique public" à l'aide de l'accesseur et d'un mot clé "statique":

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

Agent.CIRCLE; // 1

En regardant une spécification, 14.5 - Définitions de classe - vous verriez quelque chose de suspect - :)

ClassElement [Rendement]:
MéthodeDéfinition [? Rendement]
static MethodDefinition [? Yield];

Donc, à partir de là, vous pouvez suivre 14.5.14 - Sémantique d’exécution: ClassDefinitionEvaluation - pour vérifier si le résultat est vraiment comme il se présente. Plus précisément, étape 20:

  1. Pour chaque élément de classe m dans l'ordre des méthodes
    1. Si IsStatic of m est false , alors
      1. Soit status le résultat de l'exécution de PropertyDefinitionEvaluation pour m avec les arguments proto et false.
    2. Autre,
      1. Soit status le résultat de la réalisation de PropertyDefinitionEvaluation pour m avec les arguments F et false.
    3. Si le statut est un achèvement brutal, alors
      1. Définissez LexicalEnvironment du contexte d’exécution en cours sur Lex.
      2. Statut de retour.

IsStatic est défini plus tôt dans 14.5.9

ClassElement: statique MethodDefinition
Retourne vrai.

Donc, PropertyMethodDefinition est appelé avec "F" (constructeur, objet de fonction) comme argument, qui à son tour crée une méthode d'accesseur sur cet objet .

Ceci fonctionne déjà dans au moins IETP (aperçu technique), ainsi que les compilateurs 6to5 et Traceur.

125
kangax

Il existe une proposition ECMAScript de Phase 3 appelée "Champs de classe" par Daniel Ehrenberg et Jeff Morrison qui vise à résoudre ce problème.

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

Ce qui précède est équivalent à:

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;

Babelprend en charge transpiler les champs de classe par @ babel/plugin-proposition-classe-propriétés (inclus dans le stage-3 preset ), de sorte que vous puissiez utiliser cette fonctionnalité même si votre moteur d'exécution JavaScript ne la prend pas en charge.


Comparée à la solution de @ kangax consistant à déclarer un getter, cette solution peut également être plus performante, dans la mesure où la propriété est accessible directement au lieu d'appeler une fonction.

Si cette proposition est acceptée, il sera alors possible d'écrire du code JavaScript d'une manière plus proche des langages orientés objet traditionnels tels que Java et C.


Éditer : une proposition de champs de classe unifiée est maintenant à l'étape 3; mise à jour vers les packages Babel v7.x.

44
Timothy Gu

Dans les versions actuelles d’ECMAScript 6 (à partir de février 2015), toutes les propriétés de classe doivent être des méthodes et non des valeurs (remarque: dans ECMAScript, une "propriété" est similaire dans son concept à un champ OOP, sauf le champ la valeur du champ doit être un objet Function, et non une autre valeur telle que Number ou Object).

Vous pouvez toujours les spécifier à l'aide des spécificateurs de propriété de constructeur ECMAScript traditionnels:

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

Pour tirer pleinement parti de la variable statique, j’ai suivi cette approche. Pour être plus précis, nous pouvons l’utiliser pour utiliser des variables privées ou n’avoir que des getter publics, ou avoir à la fois des getter ou des setter. Dans le dernier cas, c'est la même chose que l'une des solutions présentées ci-dessus.

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'];

Je pourrais créer une autre classe prolongeant l'URL et cela a fonctionné.

J'ai utilisé babel pour convertir mon code ES6 en ES5

4
SM Adnan

La réponse de @kangax n'imite pas le comportement statique complet de OOP du langage traditionnel), car vous ne pouvez pas accéder à la propriété statique par son instance comme const agent = new Agent; agent.CIRCLE; // Undefined

Si vous souhaitez accéder à une propriété statique comme les opérations en mode POOP, voici ma solution:

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

NewApp.MULTIPLE_VERSIONS_SUPPORTED = true;

Testez le code comme suit.

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
legend80s