Actuellement dans ES5, beaucoup d'entre nous utilisent le modèle suivant dans les frameworks pour créer des classes et des variables de classe, ce qui est confortable:
// ES 5
FrameWork.Class({
variable: 'string',
variable2: true,
init: function(){
},
addItem: function(){
}
});
Dans ES6, vous pouvez créer des classes en mode natif, 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 seules les classes peuvent contenir des méthodes.
Je comprends que je peux this.myVar = true
en constructor
… mais je ne veux pas «junk» mon constructeur, surtout quand j'ai 20-30 + params pour une classe plus grande.
Je pensais à de nombreuses façons de gérer ce problème, mais je n'en ai pas encore trouvé de bonnes. (Par exemple: créez un ClassConfig
gestionnaire et passez un parameter
objet, qui est déclaré séparément de la classe. Ensuite, le gestionnaire s'attacherait à la classe. Je pensais WeakMaps
également à intégrer, d'une manière ou d'une autre.)
Quel genre d'idées auriez-vous à gérer cette situation?
la source
this.member = member
chez votre constructeur avec 20-30 paramètres?public variable2 = true
sous la classe? Cela le définirait sur le prototype.Réponses:
Mise à jour 2018:
Il y a maintenant une proposition de phase 3 - je suis impatient de rendre cette réponse obsolète dans quelques mois.
En attendant, toute personne utilisant TypeScript ou babel peut utiliser la syntaxe:
A l'intérieur d'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.
Mise à jour: Chrome 74 est désormais livré avec cette syntaxe qui fonctionne.
Les notes dans le wiki ES pour la proposition dans ES6 ( classes maximales minimales ) notent:
Cela signifie que ce que vous demandez a été pris en compte et a été explicitement rejeté.
mais pourquoi?
Bonne question. Les bonnes gens de TC39 veulent que les déclarations de classe déclarent et définissent les capacités d'une classe. Pas ses membres. Une déclaration de classe ES6 définit son contrat pour son utilisateur.
N'oubliez pas qu'une définition de classe définit des méthodes de prototype - la définition de variables sur le prototype n'est généralement pas quelque chose que vous faites. Vous pouvez bien sûr utiliser:
Dans le constructeur comme vous l'avez suggéré. Voir également le résumé du consensus .
ES7 et au-delà
Une nouvelle proposition pour ES7 est en cours d'élaboration qui permet des variables d'instance plus concises grâce aux déclarations et expressions de classe - https://esdiscuss.org/topic/es7-property-initializers
la source
static
propriétésstatic
ne fonctionne que pour les méthodes.Juste pour ajouter à la réponse de Benjamin - les variables de classe sont possibles, mais vous ne les utiliseriez pas
prototype
pour les définir.Pour une vraie variable de classe, vous voudriez faire quelque chose comme ceci:
Depuis une méthode de classe, cette variable est accessible en tant que
this.constructor.foo
(ouMyClass.foo
).Ces propriétés de classe ne sont généralement pas accessibles depuis l'instance de classe. c'est à dire
MyClass.foo
donne'bar'
maisnew MyClass().foo
estundefined
Si vous souhaitez également avoir accès à votre variable de classe à partir d'une instance, vous devrez en outre définir un getter:
Je n'ai testé cela qu'avec Traceur, mais je pense que cela fonctionnera de la même manière dans une implémentation standard.
JavaScript n'a pas vraiment de classes . Même avec ES6, nous examinons un langage basé sur un objet ou un prototype plutôt qu'un langage basé sur une classe. En tout cas
function X () {}
,X.prototype.constructor
renvoie àX
. Lorsque l'new
opérateur est utiliséX
, un nouvel objet est créé en héritantX.prototype
. Toutes les propriétés non définies dans ce nouvel objet (y comprisconstructor
) sont recherchées à partir de là. Nous pouvons penser à cela comme générant des propriétés d'objet et de classe.la source
Babel prend en charge les variables de classe dans ESNext, vérifiez cet exemple :
la source
Dans votre exemple:
Parce que MY_CONST est primitif https://developer.mozilla.org/en-US/docs/Glossary/Primitive nous pouvons simplement faire:
Mais si
MY_CONST
est le type de référence comme lastatic get MY_CONST() {return ['string'];}
sortie d'alerte est une chaîne, false . Dans ce cas, l'delete
opérateur peut faire l'affaire:Et enfin pour la variable de classe non
const
:la source
delete
opérateur, s'il est seul pour des raisons de performances. Ce que vous voulez vraiment ici, c'estObject.defineProperty
.Étant donné que votre problème est principalement stylistique (ne pas vouloir remplir le constructeur avec un tas de déclarations), il peut également être résolu stylistiquement.
De la façon dont je le vois, de nombreux langages basés sur les 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 créer une classe ES6 qui, sur le plan stylistique, a toujours du sens mais ne regroupe pas les actions typiques se déroulant dans le constructeur avec toutes les déclarations de propriétés que nous faisons. Nous utilisons simplement le constructeur JS réel comme "zone de déclaration", puis faisons une classe nommée fonction que nous traitons autrement comme la zone "autres éléments constructeurs", en l'appelant à la fin du vrai constructeur.
Les deux seront appelés lors de la construction de la nouvelle instance.
Sorta comme avoir 2 constructeurs où vous séparez les déclarations et les autres actions de constructeur que vous souhaitez entreprendre, et stylistiquement, il n'est pas trop difficile de comprendre que c'est ce qui se passe aussi.
Je trouve que c'est un style agréable à utiliser pour traiter de nombreuses déclarations et / ou de nombreuses actions devant se produire lors de l'instanciation et souhaitant garder les deux idées distinctes l'une de l'autre.
REMARQUE : Je n'utilise pas à dessein les idées idiomatiques typiques de "l'initialisation" (comme une
init()
ou uneinitialize()
méthode) car elles sont souvent utilisées différemment. Il y a une sorte de différence présumée entre l'idée de construire et d'initialiser. En travaillant avec des constructeurs, les gens savent qu'ils sont appelés automatiquement dans le cadre de l'instanciation. En voyant uneinit
méthode, beaucoup de gens vont supposer sans un second coup d'œil qu'ils doivent faire quelque chose sous la forme devar mc = MyClass(); mc.init();
, car c'est ainsi que vous initialisez généralement. Je n'essaie pas d'ajouter un processus d'initialisation pour l'utilisateur de la classe, j'essaie d'ajouter au processus de construction de la classe elle-même.Alors que certaines personnes peuvent faire une double prise pendant un moment, c'est en fait le problème: cela leur communique que l'intention fait partie de la construction, même si cela les oblige à faire une double prise et à aller "ce n'est pas comment les constructeurs ES6 fonctionnent "et prennent une seconde en regardant le constructeur réel pour aller" oh, ils l'appellent en bas, je vois ", c'est bien mieux que de NE PAS communiquer cette intention (ou de la communiquer incorrectement) et d'obtenir probablement beaucoup de les gens l'utilisant mal, essayant de l'initialiser de l'extérieur et de la camelote. C'est très intentionnel au schéma que je suggère.
Pour ceux qui ne veulent pas suivre ce modèle, l'exact opposé peut également fonctionner. Fermez les déclarations vers une autre fonction au début. Peut-être le nommer "propriétés" ou "publicProperties" ou quelque chose. Ensuite, mettez le reste des choses dans le constructeur normal.
Notez que cette deuxième méthode peut sembler plus propre, mais elle a également un problème inhérent où elle
properties
est remplacée lorsqu'une classe utilisant cette méthode en étend une autre. Il faudrait donner des noms plus uniquesproperties
pour éviter cela. Ma première méthode n'a pas ce problème car sa fausse moitié du constructeur porte le nom unique de la classe.la source
init
.init
est un motif utilisé souvent, et souvent destiné à être appelé de l'extérieur de la classe lorsque l'utilisateur extérieur veut s'initialiser, c'est-à-direvar b = new Thing(); b.init();
. C'est un choix 100% stylistique que je préférerais communiquer, c'est une 2ème fonction qui est automatiquement appelée en profitant des motifs trouvés dans d'autres langues. Il est beaucoup moins probable que quelqu'un regarde cela et suppose qu'il doit appeler laMyClass
méthode de l'extérieur, plus probable qu'il réalisera que l'intention est une 2ème méthode agissant dans la construction (c'est-à-dire appelée par elle-même lors de l'instanciation).MyClass
.$myvar
façon standard de se référer aux variables destinées à contenir des objets jQuery n'est pas similaire au modèle d'avoir $ au début des variables auquel tant de programmeurs PHP sont habitués. Juste cette petite implication que "oui, ce n'est pas exactement la même chose ... mais regardez ... c'est toujours une variable parce que c'est ainsi que les variables sont faites dans certaines langues!" aide.properties()
fonction. Cependant, les choses deviennent un peu plus compliquées lors de l'extension des classes, donc je suggère que si vous utilisez uneproperties()
fonction dans toutes vos classes pour vous déclarer les propriétés de classe que vous préfixez le nom de la méthode avec le nom de la classe (IE:MyClassProperties()
afin d'éviter de remplacer accidentellement les appels de fonction dans les sous-classes. N'oubliez pas non plus que tous les appels àsuper()
doivent être déclarés en premier dans le constructeur de la classe.Et la façon oldschool?
Dans constructeur, vous ne mentionnez que les variables qui doivent être calculées. J'aime l'héritage des prototypes pour cette fonctionnalité - cela peut aider à économiser beaucoup de mémoire (au cas où il y aurait beaucoup de vars jamais assignés).
la source
Comme Benjamin l'a dit dans sa réponse, TC39 a explicitement décidé de ne pas inclure cette fonctionnalité au moins pour ES2015. Cependant, le consensus semble être qu'ils l'ajouteront dans ES2016.
La syntaxe n'a pas encore été décidée, mais il existe 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. Activez la transformation des propriétés de classe selon ces instructions et vous êtes prêt à partir. Voici un exemple de syntaxe:
Cette proposition est à un stade très précoce, alors soyez prêt à modifier votre syntaxe au fil du temps.
la source
[Long thread, je ne sais pas si c'est déjà répertorié en option ...].
Une alternative simple pour les conants uniquement , serait de définir la const en dehors de la classe. Cela ne sera accessible qu'à partir du module lui-même, sauf s'il est accompagné d'un getter.
Cette façon
prototype
n'est pas jonchée et vous obtenez leconst
.la source
var
au lieu deconst
2) utilisez plusieurs instances de cette classe? Ensuite, les variables externes seront modifiées à chaque instance de la classeES7
syntaxe des membres de la classe:ES7
a une solution pour «joncher» votre fonction constructeur. Voici un exemple:L'exemple ci-dessus se présenterait comme suit dans
ES6
:Soyez conscient lorsque vous utilisez cela que cette syntaxe pourrait ne pas être prise en charge par tous les navigateurs et pourrait devoir être transposée dans une version antérieure de JS.
Bonus: une fabrique d'objets:
la source
Vous pouvez imiter le comportement des classes es6 ... et utiliser vos variables de classe :)
Je l'ai mis sur GitHub
la source
La façon dont j'ai résolu cela, qui est une autre option (si jQuery est disponible), était de définir les champs dans un objet old-school, puis d'étendre la classe avec cet objet. Je ne voulais pas non plus poivrer le constructeur avec des affectations, cela semblait être une bonne solution.
- Mise à jour suite au commentaire de Bergi.
Pas de version JQuery:
Vous vous retrouvez toujours avec un constructeur «gras», mais au moins c'est tout dans une classe et assigné en un seul coup.
EDIT # 2: J'ai maintenant bouclé la boucle et j'attribue maintenant des valeurs au constructeur, par exemple
Pourquoi? Très simple, en utilisant ce qui précède et quelques commentaires JSdoc, PHPStorm a pu effectuer l'achèvement du code sur les propriétés. Assigner toutes les vars en un seul coup était bien, mais l'impossibilité de coder pour compléter les propriétés, imo, ne vaut pas l'avantage de performance (presque certainement minuscule).
la source
class
syntaxe, vous pouvez aussi le faireObject.assign
. Pas besoin de jQuery.MyClassFields
un constructeur - il n'a pas de méthode. Pouvez-vous expliquer pourquoi vous avez fait cela (au lieu, disons, d'une simple fonction d'usine qui renvoie un objet littéral)?Eh bien, vous pouvez déclarer des variables à l'intérieur du constructeur.
la source
Vous ne pouvez toujours pas déclarer de classes comme dans un autre langage de programmation. Mais vous pouvez créer autant de variables de classe. Mais le problème est la portée de l'objet de classe. Donc selon moi, la meilleure programmation OOP en Javascript ES6: -
la source
Ceci est un combo un peu hack de statique et fonctionne pour moi
ailleurs utilisé
la source
singleton_instance
comme nom de propriété pour que tout le monde comprenne ce que vous faites ici.