Classe ES6 variables alternatives

actuellement dans ES5 beaucoup d'entre nous utilisent le modèle suivant dans les cadres pour créer des classes et des variables de classe, qui est confortable:

// ES 5
FrameWork.Class({

    variable: 'string',
    variable2: true,

    init: function(){

    },

    addItem: function(){

    }

});

dans ES6 vous pouvez créer des classes nativement, mais il n'y a pas d'option pour avoir des variables de classe:

// ES6
class MyClass {
    const MY_CONST = 'string'; // <-- this is not possible in ES6
    constructor(){
        this.MY_CONST;
    }
}

malheureusement, ce qui précède ne fonctionnera pas, car les classes ne peuvent contenir que des méthodes.

je comprends que je peux this.myVar = true dans constructor ... mais je ne veux pas "junk" mon constructeur, surtout quand j'ai 20-30+ params pour une plus grande classe.

je pensais à plusieurs façons de traiter cette question, mais je n'en ai pas encore trouvé de bonnes. (Par exemple: créer un handler ClassConfig , et passer un objet parameter , qui est déclaré séparément de la classe. Alors le manipulateur s'attachait à la classe. Je pensais à WeakMaps aussi pour intégrer, d'une façon ou d'une autre.)

Quel genre d'idées auriez-vous pour gérer cela situation?

414
demandé sur Zearin 2014-03-20 13:45:42

13 réponses

2018 Mise à jour:

il y a maintenant une proposition de phase 3 - je me réjouis de rendre cette réponse obsolète dans quelques mois.

entre-temps, quiconque utilise le script ou babel peut utiliser la syntaxe:

varName = value

dans un corps de déclaration/expression de classe et il définira une variable. J'espère que dans quelques mois / semaines je pourrai publier une mise à jour.


les notes le wiki ES pour la proposition dans ES6 ( classes minimales maximales ) note:

il n'y a pas (intentionnellement) de moyen déclaratif direct pour définir soit les propriétés des données du prototype (autres que les propriétés des méthodes), soit la propriété de la classe de l'instance

Les propriétés de la classe

et les propriétés des données prototypes doivent être créées en dehors de la déclaration.

les biens visés par la définition d'une catégorie sont les suivants: attribué les mêmes attributs que si elles apparaissaient dans un objet littéral.

cela signifie que ce que vous demandez a été considéré, et explicitement décidé contre.

mais... pourquoi?

bonne question. Les bonnes gens de TC39 veulent des déclarations de classe pour déclarer et définir les capacités d'une classe. Pas de ses membres. Une déclaration de classe ES6 définit son contrat pour son utilisateur.

rappelez - vous, une définition de classe définit prototype méthodes-définir des variables sur le prototype n'est généralement pas quelque chose que vous faites. Vous pouvez bien sûr utiliser:

constructor(){
    this.foo = bar
}

Dans le constructeur comme vous l'avez suggéré. Voir aussi le résumé du consensus .

ES7 et au-delà

une nouvelle proposition pour ES7 est en cours d'élaboration qui permet de variables d'instance à travers les déclarations de classe et les expressions - https://esdiscuss.org/topic/es7-property-initializers

444
répondu Benjamin Gruenbaum 2018-06-10 09:02:08

juste pour ajouter aux variables de classe de réponse de Benjamin sont possibles, mais vous ne voudriez pas utiliser prototype pour les définir.

pour une vraie variable de classe, vous voudriez faire quelque chose comme ceci:

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

à l'intérieur d'une méthode de classe que la variable peut être consultée comme this.constructor.foo (ou MyClass.foo ).

ces propriétés de classe ne seraient généralement pas accessibles à partir de l'instance de classe. c'est à dire MyClass.foo donne 'bar' mais new MyClass().foo est undefined

si vous voulez aussi avoir accès à votre variable de classe à partir d'une instance, vous devrez en outre définir un getter:

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

MyClass.foo = 'bar';

Je l'ai seulement testé avec Traceur, mais je crois qu'il fonctionnera la même chose dans une implémentation standard.

JavaScript n'a pas vraiment de classes . Même avec ES6, nous regardons un objet - ou un langage basé sur un prototype plutôt qu'un langage basé sur une classe. En tout function X () {} , X.prototype.constructor points de retour à X . Lorsque l'opérateur new est utilisé sur X , un nouvel objet est créé héritant de X.prototype . Toutes les propriétés non définies de ce nouvel objet (y compris constructor ) sont recherchées à partir de là. Nous pouvons penser que cela génère des propriétés d'objet et de classe.

109
répondu lyschoening 2014-10-06 09:31:14

dans votre exemple:

class MyClass {
    const MY_CONST = 'string';
    constructor(){
        this.MY_CONST;
    }
}

à cause de MY_CONST est primitif https://developer.mozilla.org/en-US/docs/Glossary/Primitive we can just do:

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

mais si MY_CONST est un type de référence comme static get MY_CONST() {return ['string'];} la sortie d'alerte est chaîne, faux . Dans ce cas delete l'opérateur peut faire l'affaire:

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

et enfin pour la variable de classe pas 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
répondu Oleg Mazko 2015-10-15 07:35:21

Babel supporte les variables de classe dans ESNext, cochez cette case exemple :

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'
20
répondu Kosmetika 2018-04-15 18:58:02

puisque votre problème est principalement stylistique (ne pas vouloir remplir le constructeur avec un tas de déclarations) il peut être résolu stylistiquement aussi bien.

de la façon dont je le vois, de nombreux langages basés sur des classes ont le constructeur être une fonction nommée d'après le nom de classe lui-même. Stylistiquement nous pourrions utiliser cela pour faire une classe ES6 qui stylistiquement encore fait du sens mais ne Groupe pas les actions typiques ayant lieu dans le constructeur avec toute la propriété les déclarations que nous faisons. Nous utilisons simplement le constructeur réel JS comme la" zone de déclaration", puis faisons une fonction de classe nommée que nous traitons autrement comme la zone" autre chose constructeur", l'appelant à la fin du constructeur vrai.

"use strict";

class MyClass
{
    // only declare your properties and then call this.ClassName(); from here
    constructor(){
        this.prop1 = 'blah 1';
        this.prop2 = 'blah 2';
        this.prop3 = 'blah 3';
        this.MyClass();
    }

    // all sorts of other "constructor" stuff, no longer jumbled with declarations
    MyClass() {
        doWhatever();
    }
}

les deux seront appelés comme la nouvelle instance est construite.

comme avoir 2 constructeurs où vous séparez les déclarations et les autres actions du constructeur que vous voulez prendre, et stylistiquement rend-il pas trop dur à comprendre c'est ce qui se passe.

je trouve que c'est un style agréable à utiliser lorsque l'on fait face à beaucoup de déclarations et/ou beaucoup d'actions qui doivent se produire sur instanciation et qui veulent garder les deux idées distinctes l'une de l'autre.


NOTE : Je n'utilise pas très délibérément les idées idiomatiques typiques de "l'initialisation" (comme un init() ou initialize() ) parce que ceux-ci sont souvent utilisés différemment. Il y a une sorte de différence présumée entre l'idée de construire et d'initialiser. Travailler avec les constructeurs les gens savent qu'ils sont appelés automatiquement dans le cadre de l'instanciation. Voir une méthode init beaucoup de gens vont supposer sans un second coup d'oeil qu'ils ont besoin de faire quelque chose le long de la forme de var mc = MyClass(); mc.init(); , parce que c'est la façon dont vous initialisez généralement. Je ne suis pas d'essayer d'ajouter une phase d'initialisation processus pour l'utilisateur de la classe, j'essaie d'ajouter à le processus de construction de la classe elle-même.

alors que certaines personnes peuvent faire une double prise pour un moment, c'est en fait le point: il leur communique que l'intention fait partie de la construction, même si cela leur fait faire un peu d'une double prise et aller "ce n'est pas comme ça que les constructeurs ES6 travaillent" et prendre une seconde en regardant le constructeur réel pour aller "oh, ils l'appellent à la en bas, je vois", c'est beaucoup mieux que de ne PAS communiquer cette intention (ou incorrecte de la communication) et probablement beaucoup de personnes utilisant ce mal, en essayant de les initialiser à partir de l'extérieur et d'ordure. C'est tout à fait intentionnel au schéma que je suggère.


Pour ceux qui ne veulent pas suivre ce modèle, l'inverse peut fonctionner aussi. Au début, les déclarations sont affectées à une autre fonction. Peut-être l'appeler "Propriétés" ou "propriétés publiques" ou quelque chose comme ça. Ensuite, mettre le reste de la substance dans le constructeur normal.

"use strict";

class MyClass
{
    properties() {
        this.prop1 = 'blah 1';
        this.prop2 = 'blah 2';
        this.prop3 = 'blah 3';
    }

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

noter que cette deuxième méthode peut sembler plus propre, mais il a également un problème inhérent où properties obtient dépassé comme une classe utilisant cette méthode étend une autre. Il faudrait donner plus de noms uniques à properties pour éviter ça. Ma première méthode n'a pas ce problème parce que son faux moitié du constructeur est unique nommé d'après la classe.

13
répondu Jimbo Jonny 2016-10-27 01:33:16

Qu'en est-il de la voie de la vieille école?

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

dans le constructeur, vous mentionnez seulement les vars qui doivent être calculés. J'aime l'héritage de prototype pour cette fonctionnalité -- il peut aider à sauver beaucoup de mémoire(dans le cas où il ya beaucoup de vars jamais-assignés).

12
répondu zarkone 2014-04-10 12:33:32

comme Benjamin L'a dit dans sa réponse, TC39 a explicitement décidé de ne pas inclure cette caractéristique au moins pour ES2015. Toutefois, le consensus semble être qu'ils vont l'ajouter dans ES2016.

la syntaxe n'a pas encore été décidée, mais il y a une proposition préliminaire pour ES2016 qui vous permettra de déclarer des propriétés statiques sur une classe.

Grâce à la magie de babel, vous pouvez l'utiliser aujourd'hui. Activer les propriétés de la classe transformez selon ces instructions et vous êtes prêt à partir. Voici un exemple de syntaxe:

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

cette proposition est dans un état très précoce, alors soyez prêt à modifier votre syntaxe au fil du temps.

11
répondu BonsaiOak 2015-12-17 12:19:30

vous pouvez imiter le comportement des classes es6... et utilisez vos variables de classe:)

regarde maman... pas de cours!

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

Je l'ai mis sur GitHub

5
répondu Ruslan 2017-05-29 02:36:51

[Long fil, vous ne savez pas si sa déjà dans la liste des options...].

Une simple alternative serait de définir la const à l'extérieur de la classe. Il ne sera accessible qu'à partir du module lui-même, à moins d'être accompagné d'un getter.

De cette façon, prototype n'est pas pollué et vous obtenez le 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);
    }
}
5
répondu Hertzel Guinness 2018-02-21 07:35:45

pouvez-vous éviter toute la question en utilisant des littérales fortes et une petite bibliothèque de modèles logique exécuter contenu dans une fermeture plus grande?

en ignorant la fermeture pour l'instant

const myDynamicInputs=(items)=>\backtick -${ items.map((item, I, array)=>'${do tons of junk}').join('')}';

http://codepen.io/jfrazz/pen/BQJPBZ /

c'est l'exemple le plus simple que je puisse offrir à partir du dépôt, les 400 premières lignes sont une bibliothèque de données+ Quelques fonctions utilitaires de base. En Plus d'une poignée d'utilité constant.

après la plaque chauffante, que nous transformons en uri de données-téléchargé par les utilisateurs de l'application-nous avons des gabarits de tableaux, qui doivent être levés et redéployés, mais qui peuvent être composés pour être n'importe quoi des entrées, des chutes, ou 52 pages de questions et de données.

C'est ce second exemple: manger un objet, obtenir des entrées de différents types, tous en utilisant const comme variable de base de la bibliothèque, étant construit.

http://codepen.io/jfrazz/pen/rWprVR /

pas exactement ce que vous avez demandé, mais une démonstration claire que constante peut être assez dynamique.

0
répondu Jason Frazzano 2016-12-02 04:57:43

la façon dont j'ai résolu ce, qui est une autre option (si vous avez jQuery disponible), était de définir les champs dans un objet de la vieille école et puis étendre la classe avec cet objet. Je ne voulais pas non plus assaisonner le constructeur avec des missions, cela semblait être une solution soignée.

function MyClassFields(){
    this.createdAt = new Date();
}

MyClassFields.prototype = {
    id : '',
    type : '',
    title : '',
    createdAt : null,
};

class MyClass {
    constructor() {
        $.extend(this,new MyClassFields());
    }
};

-- mise à Jour de la Suite Bergi commentaire.

Pas De Version JQuery:

class SavedSearch  {
    constructor() {
        Object.assign(this,{
            id : '',
            type : '',
            title : '',
            createdAt: new Date(),
        });

    }
}

vous finissez toujours avec le constructeur 'fat' , mais au moins c'est tout dans une classe et assigné dans un coup.

EDIT #2: J'ai bouclé la boucle et je suis en train d'attribuer des valeurs au constructeur, par exemple

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

pourquoi? Simple really, en utilisant ce qui précède plus quelques commentaires JSdoc, PHPStorm a été en mesure d'effectuer l'achèvement de code sur les propriétés. Affectation de tous les vars en un seul coup, ce qui était agréable, mais l'impossibilité de code complète les propriétés, l'omi, n'est pas la peine de le (presque certainement minuscule) avantage de performance.

0
répondu Steve Childs 2017-05-17 22:41:18

Eh bien, vous pouvez déclarer des variables à l'intérieur du constructeur.

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

var foo = new Foo()

foo.method()
0
répondu Osama Xäwãñz 2018-06-21 08:17:02

c'est un peu hackish combo de statique et obtenir des travaux pour moi

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

utilisé ailleurs

var conf = ConstantThingy.NO_REENTER__INIT;
if(conf.init)...
-1
répondu TroyWorks 2015-09-12 02:57:49