Est-il possible de créer des propriétés privées dans les classes ES6?
Voici un exemple. Comment puis-je empêcher l'accès à instance.property
?
class Something {
constructor(){
this.property = "test";
}
}
var instance = new Something();
console.log(instance.property); //=> "test"
Réponses:
Les champs (et méthodes) privés sont en cours de mise en œuvre dans la norme ECMA . Vous pouvez commencer à les utiliser dès aujourd'hui avec les préréglages babel 7 et stage 3.
la source
this
dans le constructeur avant d'appelersuper()
. Pourtant babel les met avant super.#privateCrap
syntaxe?#beep() {}
:; et ceci:async #bzzzt() {}
?Réponse courte, non, il n'y a pas de support natif pour les propriétés privées avec les classes ES6.
Mais vous pouvez imiter ce comportement en n'attachant pas les nouvelles propriétés à l'objet, mais en les conservant dans un constructeur de classe, et en utilisant des getters et des setters pour atteindre les propriétés cachées. Notez que les getters et setters sont redéfinis sur chaque nouvelle instance de la classe.
ES6
ES5
la source
class
syntaxe en premier lieu.getName
etsetName
privées?Pour développer la réponse de @ loganfsmyth:
Les seules données vraiment privées en JavaScript sont toujours des variables de portée. Vous ne pouvez pas avoir de propriétés privées au sens de propriétés accessibles en interne de la même manière que les propriétés publiques, mais vous pouvez utiliser des variables de portée pour stocker des données privées.
Variables de portée
L'approche ici consiste à utiliser la portée de la fonction constructeur, qui est privée, pour stocker des données privées. Pour que les méthodes aient accès à ces données privées, elles doivent également être créées dans le constructeur, ce qui signifie que vous les recréez avec chaque instance. Il s'agit d'une pénalité liée aux performances et à la mémoire, mais certains pensent que la pénalité est acceptable. La pénalité peut être évitée pour les méthodes qui n'ont pas besoin d'accéder aux données privées en les ajoutant au prototype comme d'habitude.
Exemple:
Scope WeakMap
Un WeakMap peut être utilisé pour éviter les performances et la pénalité de mémoire de l'approche précédente. Les WeakMaps associent les données aux objets (ici, les instances) de manière à ce qu'elles ne soient accessibles qu'en utilisant ce WeakMap. Ainsi, nous utilisons la méthode des variables de portée pour créer un WeakMap privé, puis utilisons ce WeakMap pour récupérer les données privées associées à
this
. C'est plus rapide que la méthode des variables de portée car toutes vos instances peuvent partager une seule WeakMap, vous n'avez donc pas besoin de recréer des méthodes juste pour leur faire accéder à leurs propres WeakMaps.Exemple:
Cet exemple utilise un objet pour utiliser un WeakMap pour plusieurs propriétés privées; vous pouvez également utiliser plusieurs WeakMaps et les utiliser comme
age.set(this, 20)
, ou écrire un petit wrapper et l'utiliser d'une autre manière, commeprivateProps.set(this, 'age', 0)
.La confidentialité de cette approche pourrait théoriquement être violée en altérant l'
WeakMap
objet global . Cela dit, tout JavaScript peut être brisé par des globaux mutilés. Notre code est déjà construit sur l'hypothèse que cela ne se produit pas.(Cette méthode pourrait également être utilisée avec
Map
, mais elleWeakMap
est meilleure carMap
elle créera des fuites de mémoire à moins que vous ne soyez très prudent, et à cette fin, les deux ne sont pas autrement différents.)Demi-réponse: symboles de portée
Un symbole est un type de valeur primitive qui peut servir de nom de propriété. Vous pouvez utiliser la méthode des variables de portée pour créer un symbole privé, puis stocker des données privées sur
this[mySymbol]
.La confidentialité de cette méthode peut être violée à l'aide
Object.getOwnPropertySymbols
, mais est quelque peu difficile à faire.Exemple:
Demi-réponse: Souligne
L'ancien défaut, utilisez simplement une propriété publique avec un préfixe de soulignement. Bien qu'elle ne soit en aucun cas une propriété privée, cette convention est suffisamment répandue pour faire un bon travail en communiquant que les lecteurs doivent traiter la propriété comme privée, ce qui fait souvent le travail. En échange de ce laps de temps, nous obtenons une approche plus facile à lire, plus facile à taper et plus rapide.
Exemple:
Conclusion
Depuis ES2017, il n'y a toujours pas de moyen idéal pour faire des propriétés privées. Diverses approches ont des avantages et des inconvénients. Les variables de portée sont vraiment privées; les WeakMaps de portée sont très privés et plus pratiques que les variables de portée; les symboles de portée sont raisonnablement privés et raisonnablement pratiques; les soulignés sont souvent assez privés et très pratiques.
la source
instanceof
. J'avoue que je pensais à cette approche comme incluse uniquement dans un souci d'exhaustivité et que j'aurais dû réfléchir davantage à ses capacités réelles.Mise à jour: une proposition avec une syntaxe plus agréable est en cours. Les contributions sont les bienvenues.
Oui, il existe - pour un accès limité
Symbol
aux objets - ES6 introduit l' art .Les symboles sont uniques, vous ne pouvez pas y accéder de l'extérieur sauf avec la réflexion (comme les privés en Java / C #) mais toute personne ayant accès à un symbole à l'intérieur peut l'utiliser pour accéder aux clés:
la source
Object.getOwnPropertySymbols
? ;)const myPrivateMethod = Math.random(); Something.prototype[''+myPrivateMethod] = function () { ... } new Something()[''+myPrivateMethod]();
. Ce n'est pas vraiment de la vie privée, c'est de l'obscurité, au sens du JavaScript traditionnel. Je considérerais le JavaScript «privé» comme signifiant l'utilisation de fermetures pour encapsuler des variables. Ces variables ne sont donc pas accessibles par réflexion.private
etprotected
serait beaucoup plus propre queSymbol
ouName
. Je préfère la notation par points plutôt que la notation par crochets. Je voudrais continuer à utiliser un point pour des choses privées.this.privateVar
La réponse est non". Mais vous pouvez créer un accès privé à des propriétés comme celle-ci:
export
mot clé.(La suggestion que les symboles pourraient être utilisés pour garantir la confidentialité était vraie dans une version antérieure de la spécification ES6 mais n'est plus le cas: https://mail.mozilla.org/pipermail/es-discuss/2014-January/035604. html et https://stackoverflow.com/a/22280202/1282216 . Pour une discussion plus longue sur les symboles et la confidentialité, voir: https://curiosity-driven.org/private-properties-in-javascript )
la source
La seule façon d'obtenir une véritable confidentialité dans JS est par l'étendue, il n'y a donc aucun moyen d'avoir une propriété qui en est membre
this
qui ne sera accessible qu'à l'intérieur du composant. La meilleure façon de stocker des données véritablement privées dans ES6 est avec un WeakMap.Évidemment, c'est probablement un processus lent et certainement moche, mais cela offre de l'intimité.
Gardez à l'esprit que CECI N'EST PAS parfait, car Javascript est tellement dynamique. Quelqu'un pourrait encore faire
pour capturer les valeurs au fur et à mesure qu'elles sont stockées, donc si vous voulez être très prudent, vous devez capturer une référence locale
.set
et l'.get
utiliser explicitement au lieu de vous fier au prototype remplaçable.la source
get
à une par méthode (par exempleconst _ = privates.get(this); console.log(_.privateProp1);
).const myObj = new SomeClass(); console.log(privateProp1.get(myObj)) // "I am Private1"
cela signifie que votre propriété est privée ou non?Pour référence future d'autres sur les spectateurs, j'entends maintenant que la recommandation est d'utiliser WeakMaps pour conserver des données privées.
Voici un exemple de travail plus clair:
la source
Dépend de qui vous demandez :-)
Aucun
private
modificateur de propriété n'est inclus dans la proposition de classes minimalement maximales qui semble avoir fait partie du brouillon actuel .Cependant, les noms privés peuvent être pris en charge , ce qui autorise les propriétés privées - et ils pourraient probablement être également utilisés dans les définitions de classe.
la source
L'utilisation de modules ES6 (initialement proposée par @ d13) fonctionne bien pour moi. Il n'imite pas parfaitement les propriétés privées, mais au moins vous pouvez être sûr que les propriétés qui devraient être privées ne fuiront pas en dehors de votre classe. Voici un exemple:
quelque chose.js
Le code consommateur peut alors ressembler à ceci:
Mise à jour (importante):
Comme @DanyalAytekin l'a souligné dans les commentaires, ces propriétés privées sont statiques, donc de portée globale. Ils fonctionneront bien lorsque vous travaillez avec des singletons, mais il faut faire attention aux objets transitoires. Extension de l'exemple ci-dessus:
la source
private static
.a.say(); // a
devrait êtreb.say(); // b
let _message = null
manière essayée , pas si cool, quand appelez le constructeur plusieurs fois, ça gâche.Compléter @ d13 et les commentaires de @ johnny-oshika et @DanyalAytekin:
Je suppose que dans l'exemple fourni par @ johnny-oshika, nous pourrions utiliser des fonctions normales au lieu de fonctions fléchées, puis
.bind
les utiliser avec l'objet actuel plus un_privates
objet comme paramètre curry:quelque chose.js
main.js
Avantages auxquels je peux penser:
_greet
et_updateMessage
agir comme des méthodes privées tant que nous n'avons pasexport
les références)_privates
objet liéQuelques inconvénients auxquels je peux penser:
Un extrait en cours d'exécution peut être trouvé ici: http://www.webpackbin.com/NJgI5J8lZ
la source
Oui - vous pouvez créer une propriété encapsulée , mais cela n'a pas été fait avec des modificateurs d'accès (public | privé), du moins pas avec ES6.
Voici un exemple simple de la façon dont cela peut être fait avec ES6:
1 Créer une classe en utilisant un mot de classe
2 À l'intérieur, le constructeur déclare la variable de portée de bloc en utilisant les mots réservés let OR const -> car ils sont de portée de bloc, ils ne sont pas accessibles de l'extérieur (encapsulés)
3 Pour autoriser un certain contrôle d'accès (setters | getters) à ces variables, vous pouvez déclarer la méthode d'instance à l'intérieur de son constructeur en utilisant:
this.methodName=function(){}
syntaxVérifions maintenant:
la source
new Something();
car vos méthodes sont déclarées dans le constructeur pour y avoir accès variables privées. Cela peut entraîner une grande consommation de mémoire si vous créez beaucoup d'instances de votre classe, donc des problèmes de performances. Les méthodes auraient dû être déclarées en dehors de la portée du constructeur. Mon commentaire était plus une explication des inconvénients de votre solution qu'une critique.Une approche différente du «privé»
Au lieu de lutter contre le fait que la visibilité privée n'est actuellement pas disponible dans ES6, j'ai décidé d'adopter une approche plus pratique qui fonctionne très bien si votre IDE prend en charge JSDoc (par exemple, Webstorm). L'idée est d'utiliser la
@private
balise . En ce qui concerne le développement, l'IDE vous empêchera d'accéder à tout membre privé en dehors de sa classe. Fonctionne assez bien pour moi et cela a été vraiment utile pour masquer les méthodes internes, donc la fonction de saisie semi-automatique me montre exactement ce que la classe voulait vraiment exposer. Voici un exemple:la source
@private
commentaire ne peut pas les empêcher, c'est seulement une fonctionnalité pour la génération de documentation et vous êtes IDE.WeakMap
Object.getOwnPropertySymbols
)Tout d'abord, définissez une fonction pour envelopper WeakMap:
Ensuite, construisez une référence en dehors de votre classe:
Remarque: la classe n'est pas prise en charge par IE11, mais elle semble plus propre dans l'exemple.
la source
Oh, tant de solutions exotiques! Je ne me soucie généralement pas de la confidentialité, j'utilise donc la "pseudo confidentialité" comme il est dit ici . Mais si vous vous en souciez (s'il y a des exigences particulières pour cela), j'utilise quelque chose comme dans cet exemple:
Une autre implémentation possible de la fonction (constructeur)
Job
:la source
Personnellement, j'aime la proposition de l' opérateur de liaison
::
et je la combinerais alors avec la solution @ d13 mentionnée, mais pour l'instant je m'en tiens à la réponse de @ d13 où vous utilisez leexport
mot - clé pour votre classe et placez les fonctions privées dans le module.il y a une autre solution difficile qui n'a pas été mentionnée ici qui suit est une approche plus fonctionnelle et lui permettrait d'avoir tous les accessoires / méthodes privés au sein de la classe.
Private.js
Test.js
des commentaires à ce sujet seraient appréciés.
la source
Je suis tombé sur ce post en cherchant la meilleure pratique pour les "données privées pour les classes". Il a été mentionné que certains des modèles auraient des problèmes de performances.
J'ai rassemblé quelques tests jsperf basés sur les 4 modèles principaux du livre en ligne "Exploring ES6":
http://exploringjs.com/es6/ch_classes.html#sec_private-data-for-classes
Les tests peuvent être trouvés ici:
https://jsperf.com/private-data-for-classes
Dans Chrome 63.0.3239 / Mac OS X 10.11.6, les modèles les plus performants étaient "Données privées via des environnements constructeurs" et "Données privées via une convention de dénomination". Pour moi, Safari a bien fonctionné pour WeakMap mais Chrome pas si bien.
Je ne connais pas l'impact de la mémoire, mais le modèle des "environnements constructeurs" dont certains avaient averti qu'il s'agissait d'un problème de performances était très performant.
Les 4 modèles de base sont:
Données privées via des environnements constructeurs
Données privées via des environnements constructeurs 2
Données privées via une convention de nommage
Données privées via WeakMaps
Données privées via des symboles
la source
Je crois qu'il est possible d'obtenir le «meilleur des deux mondes» en utilisant des fermetures à l'intérieur des constructeurs. Il existe deux variantes:
Tous les membres des données sont privés
Certains membres sont privés
REMARQUE: c'est certes moche. Si vous connaissez une meilleure solution, veuillez modifier cette réponse.
la source
En fait, il est possible d'utiliser des symboles et des proxys. Vous utilisez les symboles dans la portée de la classe et définissez deux interruptions dans un proxy: un pour le prototype de classe afin que Reflect.ownKeys (instance) ou Object.getOwnPropertySymbols ne donne pas vos symboles, l'autre pour le constructeur lui-même donc quand
new ClassName(attrs)
est appelé, l'instance retournée sera interceptée et verra ses propres symboles de propriétés bloqués. Voici le code:Reflect.ownKeys()
fonctionne comme ça:Object.getOwnPropertyNames(myObj).concat(Object.getOwnPropertySymbols(myObj))
c'est pourquoi nous avons besoin d'un piège pour ces objets.la source
Même Typescript ne peut pas le faire. De leur documentation :
Mais transposé sur leur terrain de jeu, cela donne:
Leur mot-clé "privé" est donc inefficace.
la source
Venant très tard à cette fête mais j'ai frappé la question OP dans une recherche donc ... Oui, vous pouvez avoir des propriétés privées en enveloppant la déclaration de classe dans une fermeture
Il y a un exemple de la façon dont j'ai des méthodes privées dans ce codepen . Dans l'extrait ci-dessous, la classe Subscribeable a deux fonctions «privées»
process
etprocessCallbacks
. Toutes les propriétés peuvent être ajoutées de cette manière et elles sont gardées privées grâce à l'utilisation de la fermeture. La confidentialité de l'OMI est un besoin rare si les préoccupations sont bien séparées et que Javascript n'a pas besoin d'être gonflé en ajoutant plus de syntaxe lorsqu'une fermeture fait le travail proprement.J'aime cette approche car elle sépare bien les préoccupations et garde les choses vraiment privées. Le seul inconvénient est la nécessité d'utiliser «soi» (ou quelque chose de similaire) pour faire référence à «ceci» dans le contenu privé.
la source
Je pense que la réponse de Benjamin est probablement la meilleure pour la plupart des cas jusqu'à ce que le langage supporte nativement les variables explicitement privées.
Cependant, si pour une raison quelconque vous devez empêcher l'accès avec
Object.getOwnPropertySymbols()
, une méthode que j'ai envisagée d'utiliser consiste à attacher une propriété unique, non configurable, non énumérable et non inscriptible qui peut être utilisée comme identificateur de propriété pour chaque objet en construction (comme un uniqueSymbol
, si vous n'avez pas déjà une autre propriété unique comme unid
). Ensuite, gardez simplement une carte des variables «privées» de chaque objet en utilisant cet identifiant.L'avantage potentiel de cette approche par rapport à l'utilisation d'un
WeakMap
est un temps d'accès plus rapide si les performances deviennent un problème.la source
destroy()
méthode qui devrait être appelée par le code using chaque fois qu'un objet doit être supprimé.Oui, tout à fait, et assez facilement aussi. Cela se fait en exposant vos variables et fonctions privées en renvoyant le graphique d'objet prototype dans le constructeur. Ce n'est pas nouveau, mais prenez un peu de js foo pour en comprendre l'élégance. De cette façon, vous n'utilisez pas de cartes globales ou faibles. C'est une forme de réflexion intégrée à la langue. Selon la façon dont vous tirez parti de cela; on peut soit forcer une exception qui interrompt la pile des appels, soit enterrer l'exception en tant que
undefined
. Ceci est démontré ci-dessous, et vous pouvez en savoir plus sur ces fonctionnalités icila source
la source
console.log(instance.property)
devrait jeter ou vous donner undefined, pas vous rendre "test".Une autre façon similaire aux deux derniers publiés
la source
La plupart des réponses disent que c'est impossible, ou vous obligent à utiliser un WeakMap ou un symbole, qui sont des fonctionnalités ES6 qui nécessiteraient probablement des polyfills. Il y a cependant une autre façon! Découvrez ceci:
J'appelle ce modèle d'accesseur de méthode . L'idée essentielle est que nous avons une fermeture , une clé à l'intérieur de la fermeture, et nous créons un objet privé (dans le constructeur) qui n'est accessible que si vous avez la clé .
Si vous êtes intéressé, vous pouvez en savoir plus à ce sujet dans mon article . En utilisant cette méthode, vous pouvez créer des propriétés par objet qui ne sont pas accessibles en dehors de la fermeture. Par conséquent, vous pouvez les utiliser dans un constructeur ou un prototype, mais pas ailleurs. Je n'ai vu cette méthode utilisée nulle part, mais je pense qu'elle est vraiment puissante.
la source
Voir cette réponse pour une solution de classe propre et simple avec une interface privée et publique et un support pour la composition
la source
J'ai trouvé une solution très simple, il suffit de l'utiliser
Object.freeze()
. Bien sûr, le problème est que vous ne pouvez rien ajouter à l'objet plus tard.la source
setName(name) { this.name = name; }
J'utilise ce modèle et cela a toujours fonctionné pour moi
la source
En fait , il est possible.
1. D'abord, créez la classe et dans le constructeur retournez la
_public
fonction appelée .2. Dans la
_public
fonction appelée , passez lathis
référence (pour obtenir l'accès à toutes les méthodes et accessoires privés) , et tous les arguments deconstructor
(qui seront passésnew Names()
).3. Dans l'
_public
étendue de la fonction, il y a aussi laNames
classe avec l'accès àthis
(_this ) référence de laNames
classe privéela source
Vous pouvez essayer ceci https://www.npmjs.com/package/private-members
Ce package enregistrera les membres par instance.
la source