Propriétés privées dans les classes JavaScript ES6

444

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"
d13
la source
5
Il existe en fait une proposition d'étape 3 pour cette fonctionnalité - tc39.github.io/proposal-class-fields github.com/tc39/proposal-class-fields
arty
@arty J'ai fourni une réponse à cela avec des exemples: stackoverflow.com/a/52237988/1432509
Alister le

Réponses:

165

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.

class Something {
  #property;

  constructor(){
    this.#property = "test";
  }

  #privateMethod() {
    return 'hello world';
  }

  getPrivateMessage() {
      return this.#privateMethod();
  }
}

const instance = new Something();
console.log(instance.property); //=> undefined
console.log(instance.privateMethod); //=> undefined
console.log(instance.getPrivateMessage()); //=> hello world
Alister
la source
Je me demande comment ces champs de classe peuvent fonctionner. Vous ne pouvez pas actuellement utiliser thisdans le constructeur avant d'appeler super(). Pourtant babel les met avant super.
seeker_of_bacon
Comment configurer ESLint pour autoriser la #privateCrapsyntaxe?
Marecky
6
Et l'eslint? J'ai eu une erreur d'analyse au signe égal. Babel fonctionne, juste eslint ne peut pas analyser cette nouvelle syntaxe js.
martonx
6
Wow c'est très moche. Hashtag est un caractère valide. La propriété n'est pas vraiment privée, ou? .. Je l'ai vérifié dans TypeScript. Les membres privés ne sont pas compilés en privé ou en lecture seule (de l'extérieur). Juste déclaré comme une autre propriété (publique). (ES5).
Dominik
2
Comment écrivez-vous des méthodes privées avec cela? Puis-je faire ceci #beep() {}:; et ceci: async #bzzzt() {}?
Константин Ван
277

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

class Person {
    constructor(name) {
        var _name = name
        this.setName = function(name) { _name = name; }
        this.getName = function() { return _name; }
    }
}

ES5

function Person(name) {
    var _name = name
    this.setName = function(name) { _name = name; }
    this.getName = function() { return _name; }
}
MetalGodwin
la source
1
J'aime le mieux cette solution. Je suis d'accord qu'il ne devrait pas être utilisé pour la mise à l'échelle, mais il est parfait pour les classes qui ne seront généralement instanciées qu'une fois par inclusion.
Blake Regalia du
2
Vous redéfinissez également chaque composant de cette classe chaque fois qu'un nouveau est créé.
Quentin Roy
10
Cela est tellement bizarre! Dans ES6, vous créez plus de "pyramides de fermeture" qu'avant ES6! La définition de fonctions DANS un constructeur semble plus laide que dans l'exemple ES5 ci-dessus.
Kokodoko
1
Étant donné que l'OP pose spécifiquement des questions sur les classes ES6, je pense personnellement que c'est une mauvaise solution, même si cela fonctionne techniquement. La principale limitation est que désormais chaque méthode de classe qui utilise des variables privées doit être déclarée à l'intérieur du constructeur, ce qui compromet gravement les avantages d'avoir la classsyntaxe en premier lieu.
NanoWizard
10
Tout cela ne fait qu'introduire l'indirection. Maintenant, comment rendre les propriétés getNameet setNameprivées?
aij
195

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:

function Person(name) {
  let age = 20; // this is private
  this.name = name; // this is public

  this.greet = function () {
    // here we can access both name and age
    console.log(`name: ${this.name}, age: ${age}`);
  };
}

let joe = new Person('Joe');
joe.greet();

// here we can access name but not age

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:

let Person = (function () {
  let privateProps = new WeakMap();

  class Person {
    constructor(name) {
      this.name = name; // this is public
      privateProps.set(this, {age: 20}); // this is private
    }

    greet() {
      // Here we can access both name and age
      console.log(`name: ${this.name}, age: ${privateProps.get(this).age}`);
    }
  }

  return Person;
})();

let joe = new Person('Joe');
joe.greet();

// here we can access joe's name but not age

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, comme privateProps.set(this, 'age', 0).

La confidentialité de cette approche pourrait théoriquement être violée en altérant l' WeakMapobjet 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 elle WeakMapest meilleure car Mapelle 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:

let Person = (function () {
  let ageKey = Symbol();

  class Person {
    constructor(name) {
      this.name = name; // this is public
      this[ageKey] = 20; // this is intended to be private
    }

    greet() {
      // Here we can access both name and age
      console.log(`name: ${this.name}, age: ${this[ageKey]}`);
    }
  }

  return Person;
})();

let joe = new Person('Joe');
joe.greet();

// Here we can access joe's name and, with a little effort, age. ageKey is
// not in scope, but we can obtain it by listing all Symbol properties on
// joe with `Object.getOwnPropertySymbols(joe)`.

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:

class Person {
  constructor(name) {
    this.name = name; // this is public
    this._age = 20; // this is intended to be private
  }

  greet() {
    // Here we can access both name and age
    console.log(`name: ${this.name}, age: ${this._age}`);
  }
}

let joe = new Person('Joe');
joe.greet();

// Here we can access both joe's name and age. But we know we aren't
// supposed to access his age, which just might stop us.

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.

tristan
la source
7
Le premier exemple d'extrait ("variables de portée") est un contre-modèle total - chaque objet renvoyé aura une classe différente. Ne fais pas ça. Si vous voulez des méthodes privilégiées, créez-les dans le constructeur.
Bergi
1
Envelopper une classe dans une fonction semble aller à l'encontre de l'objectif premier de l'utilisation des classes en premier lieu. Si vous utilisez déjà la fonction pour créer une instance, vous pouvez aussi bien placer tous vos membres privés / publics à l'intérieur de cette fonction, et oublier le mot-clé de classe entier.
Kokodoko
2
@Bergi @Kokodoko J'ai modifié l'approche des variables de portée pour être légèrement plus rapide et ne pas casser 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.
tristan
1
Excellente explication! Je suis toujours surpris que ES6 ait rendu la simulation d'une variable privée plus difficile, alors que dans ES5, vous pouviez simplement utiliser var et cela dans une fonction pour simuler le privé et le public.
Kokodoko
2
@Kokodoko Si vous renoncez à la classe et que vous mettez simplement tout dans la fonction, vous devrez également revenir à l'implémentation de l'héritage en utilisant la méthode du prototype. L'utilisation de l'extension sur les classes est de loin une approche plus propre, donc l'utilisation d'une classe à l'intérieur d'une fonction est totalement acceptable.
AndroidDev
117

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:

var property = Symbol();
class Something {
    constructor(){
        this[property] = "test";
    }
}

var instance = new Something();

console.log(instance.property); //=> undefined, can only access with access to the Symbol
Benjamin Gruenbaum
la source
6
Tu ne peux pas utiliser Object.getOwnPropertySymbols? ;)
Qantas 94 Heavy
41
@BenjaminGruenbaum: Apparemment, les symboles n'assurent plus une véritable confidentialité: stackoverflow.com/a/22280202/1282216
d13
28
@trusktr à travers trois clés? Non. A travers les symboles? Oui. Tout comme la façon dont vous pouvez utiliser la réflexion dans des langages comme C # et Java pour accéder à des champs privés. Les modificateurs d'accès ne concernent pas la sécurité - ils concernent la clarté de l'intention.
Benjamin Gruenbaum
9
Il semble que l'utilisation de symboles soit similaire à la pratique 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.
trusktr
13
De plus, je pense que l'utilisation des mots clés privateet protectedserait beaucoup plus propre que Symbolou Name. 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
trusktr
33

La réponse est non". Mais vous pouvez créer un accès privé à des propriétés comme celle-ci:

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

d13
la source
6
-1, cela ne répond pas vraiment à votre question. (Vous pouvez également utiliser des fermetures avec des IIFE dans ES5). Les propriétés privées sont énumérables par réflexion dans la plupart des langages (Java, C #, etc.). Le but des propriétés privées est de transmettre l'intention à d'autres programmes et non de faire respecter la sécurité.
Benjamin Gruenbaum
1
@BenjaminGruenbaum, je sais, j'aurais aimé avoir une meilleure réponse, je ne suis pas content non plus.
d13
Je pense que les symboles sont toujours un moyen valable pour atteindre des membres inaccessibles dans l'environnement de programmation. Oui, ils peuvent toujours être trouvés si vous le voulez vraiment, mais ce n'est pas le but, n'est-ce pas? Vous ne devez pas y stocker d'informations sensibles, mais vous ne devez pas le faire de toute façon dans le code côté client. Mais cela fonctionne dans le but de cacher une propriété ou une méthode d'une classe extérieure.
Kokodoko
L'utilisation de variables étendues au niveau d'un module comme substitut des propriétés privées dans une classe entraînera un comportement singleton.be ou un comportement similaire aux propriétés statistiques. Les instances de vars seront partagées.
Adrian Moisa
30

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 thisqui 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.

const privateProp1 = new WeakMap();
const privateProp2 = new WeakMap();

class SomeClass {
  constructor() {
    privateProp1.set(this, "I am Private1");
    privateProp2.set(this, "I am Private2");

    this.publicVar = "I am public";
    this.publicMethod = () => {
      console.log(privateProp1.get(this), privateProp2.get(this))
    };        
  }

  printPrivate() {
    console.log(privateProp1.get(this));
  }
}

É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

var oldSet = WeakMap.prototype.set;
WeakMap.prototype.set = function(key, value){
    // Store 'this', 'key', and 'value'
    return oldSet.call(this, key, value);
};

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 .setet l' .getutiliser explicitement au lieu de vous fier au prototype remplaçable.

const {set: WMSet, get: WMGet} = WeakMap.prototype;

const privateProp1 = new WeakMap();
const privateProp2 = new WeakMap();

class SomeClass {
  constructor() {
    WMSet.call(privateProp1, this, "I am Private1");
    WMSet.call(privateProp2, this, "I am Private2");

    this.publicVar = "I am public";
    this.publicMethod = () => {
      console.log(WMGet.call(privateProp1, this), WMGet.call(privateProp2, this))
    };        
  }

  printPrivate() {
    console.log(WMGet.call(privateProp1, this));
  }
}
loganfsmyth
la source
3
À titre de suggestion, vous pouvez éviter d'utiliser une carte faible par propriété en utilisant un objet comme valeur. De cette façon, vous pouvez également réduire le nombre de cartes getà une par méthode (par exemple const _ = privates.get(this); console.log(_.privateProp1);).
Quentin Roy
Ouais, c'est totalement une option aussi. Je suis surtout allé avec cela car il correspond plus directement à ce qu'un utilisateur aurait écrit lors de l'utilisation de propriétés réelles.
loganfsmyth
@loganfsmyth const myObj = new SomeClass(); console.log(privateProp1.get(myObj)) // "I am Private1"cela signifie que votre propriété est privée ou non?
Barbu Barbu
2
Pour que cela fonctionne, le code accédant à la propriété aurait besoin d'accéder à l'objet WeakMap, qui serait normalement délimité à l'intérieur d'un module et inaccessible
loganfsmyth
22

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:

function storePrivateProperties(a, b, c, d) {
  let privateData = new WeakMap;
  // unique object as key, weak map can only accept object as key, when key is no longer referened, garbage collector claims the key-value 
  let keyA = {}, keyB = {}, keyC = {}, keyD = {};

  privateData.set(keyA, a);
  privateData.set(keyB, b);
  privateData.set(keyC, c);
  privateData.set(keyD, d);

  return {
    logPrivateKey(key) {
      switch(key) {
      case "a":
        console.log(privateData.get(keyA));
        break;
      case "b":
        console.log(privateData.get(keyB));
        break;
      case "c":
        console.log(privateData.get(keyC));
        break;
      case "d":
        console.log(privateData.set(keyD));
        break;
      default:
        console.log(`There is no value for ${key}`)
      }
    }
  }
}
Communauté
la source
20
Sachez que ces propriétés sont statiques.
Michael Theriot
8
Je ne vous ai pas déçu, mais votre exemple de carte faible est complètement faux.
Benjamin Gruenbaum
4
À savoir - vous partagez les données entre toutes les instances de classe et non par instance - puis-je au moins y remédier?
Benjamin Gruenbaum
1
En effet, la carte faible doit être attachée à une instance donnée. Voir fitzgeraldnick.com/weblog/53 pour un exemple.
élargi
2
Selon MDN, les types de données primitifs tels que les symboles ne sont pas autorisés en tant que clé WeakMap. Documentation MDN WeakMap
leepowell
12

Dépend de qui vous demandez :-)

Aucun privatemodificateur 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.

Bergi
la source
3
Il est très peu probable que des noms privés parviennent à ES6, bien qu'ils pensent à une forme de chose privée pour ES7.
Qantas 94 Heavy
@ Qantas94Heavy, les noms privés et les valeurs de chaîne uniques ont été remplacés par des symboles d'après ce que je comprends.
Benjamin Gruenbaum
Oui, cela deviendra probablement des symboles. Cependant, afaik les "symboles" actuellement contenus dans la spécification ne sont utilisés que pour décrire des propriétés internes comme [[prototype]], et il n'y a aucun moyen de les créer et de les utiliser dans le code utilisateur. Connaissez-vous des documents?
Bergi
Je viens de réaliser que les modules peuvent être utilisés pour définir la confidentialité. Combiné avec des symboles qui pourraient être tout ce dont vous auriez besoin ...?
d13
1
@Cody: Votre code de module entier a sa propre portée dans ES6 de toute façon, pas besoin d'IEFE. Et oui, les symboles sont destinés à l'unicité (prévention des collisions), et non à la confidentialité.
Bergi
10

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

let _message = null;
const _greet = name => {
  console.log('Hello ' + name);
};

export default class Something {
  constructor(message) {
    _message = message;
  }

  say() {
    console.log(_message);
    _greet('Bob');
  }
};

Le code consommateur peut alors ressembler à ceci:

import Something from './something.js';

const something = new Something('Sunny day!');
something.say();
something._message; // undefined
something._greet(); // exception

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:

import Something from './something.js';
import Something2 from './something.js';

const a = new Something('a');
a.say(); // a

const b = new Something('b');
b.say(); // b

const c = new Something2('c');
c.say(); // c

a.say(); // c
b.say(); // c
c.say(); // c
Johnny Oshika
la source
4
Bon pour private static.
Danyal Aytekin
@DanyalAytekin: c'est un très bon point. Ces propriétés privées sont statiques de portée globale. J'ai mis à jour ma réponse pour refléter cela.
Johnny Oshika
Plus j'en apprends sur la programmation fonctionnelle (en particulier Elm et Haskell), plus je crois que les programmeurs JS bénéficieraient d'une approche modulaire de la "modularité" plutôt que d'une approche basée sur les classes OOP. Si nous considérons les modules ES6 comme les fondements de la création d'applications et oublions complètement les classes, je pense que nous pourrions nous retrouver avec de bien meilleures applications dans l'ensemble. Des utilisateurs expérimentés d'Elm ou de Haskell pourraient-ils commenter cette approche?
d13
1
Dans la mise à jour, le deuxième a.say(); // adevrait êtreb.say(); // b
grokky
let _message = nullmanière essayée , pas si cool, quand appelez le constructeur plusieurs fois, ça gâche.
Littlee
9

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 .bindles utiliser avec l'objet actuel plus un _privatesobjet comme paramètre curry:

quelque chose.js

function _greet(_privates) {
  return 'Hello ' + _privates.message;
}

function _updateMessage(_privates, newMessage) {
  _privates.message = newMessage;
}

export default class Something {
  constructor(message) {
    const _privates = {
      message
    };

    this.say = _greet.bind(this, _privates);
    this.updateMessage = _updateMessage.bind(this, _privates);
  }
}

main.js

import Something from './something.js';

const something = new Something('Sunny day!');

const message1 = something.say();
something.updateMessage('Cloudy day!');
const message2 = something.say();

console.log(message1 === 'Hello Sunny day!');  // true
console.log(message2 === 'Hello Cloudy day!');  // true

// the followings are not public
console.log(something._greet === undefined);  // true
console.log(something._privates === undefined);  // true
console.log(something._updateMessage === undefined);  // true

// another instance which doesn't share the _privates
const something2 = new Something('another Sunny day!');

const message3 = something2.say();

console.log(message3 === 'Hello another Sunny day!'); // true

Avantages auxquels je peux penser:

  • nous pouvons avoir des méthodes privées ( _greetet _updateMessageagir comme des méthodes privées tant que nous n'avons pas exportles références)
  • bien qu'elles ne soient pas sur le prototype, les méthodes mentionnées ci-dessus économiseront de la mémoire car les instances sont créées une fois, en dehors de la classe (au lieu de les définir dans le constructeur)
  • nous ne fuyons pas de globaux puisque nous sommes à l'intérieur d'un module
  • nous pouvons également avoir des propriétés privées en utilisant l' _privatesobjet lié

Quelques inconvénients auxquels je peux penser:

Un extrait en cours d'exécution peut être trouvé ici: http://www.webpackbin.com/NJgI5J8lZ

efidiles
la source
8

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(){}syntax

"use strict";
    class Something{
        constructor(){
            //private property
            let property="test";
            //private final (immutable) property
            const property2="test2";
            //public getter
            this.getProperty2=function(){
                return property2;
            }
            //public getter
            this.getProperty=function(){
                return property;
            }
            //public setter
            this.setProperty=function(prop){
                property=prop;
            }
        }
    }

Vérifions maintenant:

var s=new Something();
    console.log(typeof s.property);//undefined 
    s.setProperty("another");//set to encapsulated `property`
    console.log(s.getProperty());//get encapsulated `property` value
    console.log(s.getProperty2());//get encapsulated immutable `property2` value
Nikita Kurtin
la source
1
C'est (pour l'instant) la seule solution à ce problème malgré le fait que toutes les méthodes déclarées dans le constructeur sont redéclarées pour chaque instance de la classe. C'est une assez mauvaise idée concernant les performances et l'utilisation de la mémoire. Les méthodes de classe doivent être déclarées en dehors de la portée du constructeur.
Freezystem
@Freezystem First: Ce sont d' abord des méthodes d'instance (pas des méthodes de classe). La deuxième question OP était: _ Comment puis-je empêcher l'accès à instance.property?_ et ma réponse est: un exemple de comment ... Troisièmement, si vous avez une meilleure idée -
écoutons-le
1
Je ne disais pas que vous aviez tort, j'ai dit que votre solution était le meilleur compromis pour réaliser une variable privée malgré le fait qu'une copie de chaque méthode d'instance soit créée à chaque appel 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.
Freezystem
1
Mais n'est-ce pas une mauvaise pratique de définir toute votre classe à l'intérieur du constructeur? Ne sommes-nous pas en train de "pirater" javascript maintenant? Regardez simplement n'importe quel autre langage de programmation OOP, et vous verrez que le constructeur n'est pas destiné à définir une classe.
Kokodoko
1
Oui, c'est ce que je voulais dire, et votre solution fonctionne! Je dis simplement qu'en général, je suis surpris que ES6 ait ajouté un mot-clé 'class', mais supprimé la solution élégante de travailler avec var et ce, pour réaliser l'encapsulation.
Kokodoko
8

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 @privatebalise . 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:

saisie automatique affichant uniquement des éléments publics

Lucio Paiva
la source
1
Le problème est que nous ne voulons pas accéder aux variables privées via l'éditeur, nous ne voulons pas protéger les variables privées de l'extérieur - et c'est ce que fait public / privé. Si votre code est terminé, vous pouvez accéder (et l'important pense: remplacer ) à ces variables depuis l'extérieur de la classe. Votre @privatecommentaire ne peut pas les empêcher, c'est seulement une fonctionnalité pour la génération de documentation et vous êtes IDE.
Adrian Preuss
Oui, j'en suis conscient. C'est juste que cela me suffit et peut être suffisant pour les autres. Je sais que cela ne rend pas vraiment mes variables privées; cela ne fait que m'avertir de ne pas essayer d'y accéder de l'extérieur (seulement, bien sûr, si mon équipe et moi utilisons tous un IDE qui prend en charge cette fonctionnalité). Javascript (et d'autres langages, comme Python) n'a pas été conçu en fonction des niveaux d'accès. Les gens font toutes sortes de choses pour implémenter cette fonctionnalité, mais au final, nous finissons par pirater le langage pour y parvenir. J'ai décidé d'aller avec une approche plus "naturelle", si vous voulez.
Lucio Paiva
6

WeakMap

  • pris en charge dans IE11 (les symboles ne le sont pas)
  • hard-private (les accessoires utilisant des symboles sont soft-private en raison de Object.getOwnPropertySymbols)
  • peut sembler très propre (contrairement aux fermetures qui nécessitent tous les accessoires et méthodes du constructeur)

Tout d'abord, définissez une fonction pour envelopper WeakMap:

function Private() {
  const map = new WeakMap();
  return obj => {
    let props = map.get(obj);
    if (!props) {
      props = {};
      map.set(obj, props);
    }
    return props;
  };
}

Ensuite, construisez une référence en dehors de votre classe:

const p = new Private();

class Person {
  constructor(name, age) {
    this.name = name;
    p(this).age = age; // it's easy to set a private variable
  }

  getAge() {
    return p(this).age; // and get a private variable
  }
}

Remarque: la classe n'est pas prise en charge par IE11, mais elle semble plus propre dans l'exemple.

kevlened
la source
6

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:

class jobImpl{
  // public
  constructor(name){
    this.name = name;
  }
  // public
  do(time){
    console.log(`${this.name} started at ${time}`);
    this.prepare();
    this.execute();
  }
  //public
  stop(time){
    this.finish();
    console.log(`${this.name} finished at ${time}`);
  }
  // private
  prepare(){ console.log('prepare..'); }
  // private
  execute(){ console.log('execute..'); }
  // private
  finish(){ console.log('finish..'); }
}

function Job(name){
  var impl = new jobImpl(name);
  return {
    do: time => impl.do(time),
    stop: time => impl.stop(time)
  };
}

// Test:
// create class "Job"
var j = new Job("Digging a ditch");
// call public members..
j.do("08:00am");
j.stop("06:00pm");

// try to call private members or fields..
console.log(j.name); // undefined
j.execute(); // error

Une autre implémentation possible de la fonction (constructeur) Job:

function Job(name){
  var impl = new jobImpl(name);
  this.do = time => impl.do(time),
  this.stop = time => impl.stop(time)
}
Sergey
la source
5

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 le exportmot - 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

export const get = state => key => state[key];
export const set = state => (key,value) => { state[key] = value; }

Test.js

import { get, set } from './utils/Private'
export default class Test {
  constructor(initialState = {}) {
    const _set = this.set = set(initialState);
    const _get = this.get = get(initialState);

    this.set('privateMethod', () => _get('propValue'));
  }

  showProp() {
    return this.get('privateMethod')();
  }
}

let one = new Test({ propValue: 5});
let two = new Test({ propValue: 8});
two.showProp(); // 8
one.showProp(); // 5

des commentaires à ce sujet seraient appréciés.

Robin F.
la source
En général, j'aime l'approche. Commentaires: 1. vous aurez besoin d'un module private.js différent pour chaque classe pour éviter les conflits. 2. Je n'aime pas le potentiel de rendre le constructeur très long en définissant en ligne chacune de vos méthodes privées. 3. Ce serait bien si toutes les méthodes de classe étaient dans un seul fichier.
Doug Coburn
5

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

class Countdown {
    constructor(counter, action) {
        Object.assign(this, {
            dec() {
                if (counter < 1) return;
                counter--;
                if (counter === 0) {
                    action();
                }
            }
        });
    }
}
const c = new Countdown(2, () => {});
c.dec();
c.dec();

Données privées via des environnements constructeurs 2

class Countdown {
    constructor(counter, action) {
        this.dec = function dec() {
            if (counter < 1) return;
            counter--;
            if (counter === 0) {
                action();
            }
        }
    }
}
const c = new Countdown(2, () => {});
c.dec();
c.dec();

Données privées via une convention de nommage

class Countdown {
    constructor(counter, action) {
        this._counter = counter;
        this._action = action;
    }
    dec() {
        if (this._counter < 1) return;
        this._counter--;
        if (this._counter === 0) {
            this._action();
        }
    }
}
const c = new Countdown(2, () => {});
c.dec();
c.dec();

Données privées via WeakMaps

const _counter = new WeakMap();
const _action = new WeakMap();
class Countdown {
    constructor(counter, action) {
        _counter.set(this, counter);
        _action.set(this, action);
    }
    dec() {
        let counter = _counter.get(this);
        if (counter < 1) return;
        counter--;
        _counter.set(this, counter);
        if (counter === 0) {
            _action.get(this)();
        }
    }
}
const c = new Countdown(2, () => {});
c.dec();
c.dec();

Données privées via des symboles

const _counter = Symbol('counter');
const _action = Symbol('action');

class Countdown {
    constructor(counter, action) {
        this[_counter] = counter;
        this[_action] = action;
    }
    dec() {
        if (this[_counter] < 1) return;
        this[_counter]--;
        if (this[_counter] === 0) {
            this[_action]();
        }
    }
}
const c = new Countdown(2, () => {});
c.dec();
c.dec();
MarkM
la source
4

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

function myFunc() {
   console.log('Value of x: ' + this.x);
   this.myPrivateFunc();
}

function myPrivateFunc() {
   console.log('Enhanced value of x: ' + (this.x + 1));
}

class Test {
   constructor() {

      let internal = {
         x : 2,
      };
      
      internal.myPrivateFunc = myPrivateFunc.bind(internal);
      
      this.myFunc = myFunc.bind(internal);
   }
};

Certains membres sont privés

REMARQUE: c'est certes moche. Si vous connaissez une meilleure solution, veuillez modifier cette réponse.

function myFunc(priv, pub) {
   pub.y = 3; // The Test object now gets a member 'y' with value 3.
   console.log('Value of x: ' + priv.x);
   this.myPrivateFunc();
}

function myPrivateFunc() {
   pub.z = 5; // The Test object now gets a member 'z' with value 3.
   console.log('Enhanced value of x: ' + (priv.x + 1));
}

class Test {
   constructor() {
      
      let self = this;

      let internal = {
         x : 2,
      };
      
      internal.myPrivateFunc = myPrivateFunc.bind(null, internal, self);
      
      this.myFunc = myFunc.bind(null, internal, self);
   }
};

JSInitiate
la source
4

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:

const Human = (function() {
  const pet = Symbol();
  const greet = Symbol();

  const Human = privatizeSymbolsInFn(function(name) {
    this.name = name; // public
    this[pet] = 'dog'; // private 
  });

  Human.prototype = privatizeSymbolsInObj({
    [greet]() { // private
      return 'Hi there!';
    },
    revealSecrets() {
      console.log(this[greet]() + ` The pet is a ${this[pet]}`);
    }
  });

  return Human;
})();

const bob = new Human('Bob');

console.assert(bob instanceof Human);
console.assert(Reflect.ownKeys(bob).length === 1) // only ['name']
console.assert(Reflect.ownKeys(Human.prototype).length === 1 ) // only ['revealSecrets']


// Setting up the traps inside proxies:
function privatizeSymbolsInObj(target) { 
  return new Proxy(target, { ownKeys: Object.getOwnPropertyNames });
}

function privatizeSymbolsInFn(Class) {
  function construct(TargetClass, argsList) {
    const instance = new TargetClass(...argsList);
    return privatizeSymbolsInObj(instance);
  }
  return new Proxy(Class, { construct });
}

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.

Francisco Neto
la source
Merci, je vais essayer des symboles :) De toutes les réponses ci-dessus, il semble que le moyen le plus simple de créer un membre de classe inaccessible :)
Kokodoko
4

Même Typescript ne peut pas le faire. De leur documentation :

Lorsqu'un membre est marqué comme privé, il n'est pas accessible depuis l'extérieur de sa classe conteneur. Par exemple:

class Animal {
    private name: string;
    constructor(theName: string) { this.name = theName; }
}

new Animal("Cat").name; // Error: 'name' is private;

Mais transposé sur leur terrain de jeu, cela donne:

var Animal = (function () {
    function Animal(theName) {
        this.name = theName;
    }
    return Animal;
}());
console.log(new Animal("Cat").name);

Leur mot-clé "privé" est donc inefficace.

Michael Franzl
la source
2
Eh bien, il est toujours efficace car il empêche la "mauvaise" programmation, tandis que dans l'IDE. Il vous montre quels membres vous devez et ne devez pas utiliser. Je pense que c'est la principale raison de l'utilisation du privé et du public. (Par exemple, lorsque vous compilez C # en code machine, private sera-t-il toujours privé? Qui sait?). En lisant les autres réponses, il semble que l'utilisation de @Symbol peut également rendre un membre inaccessible. Mais même les symboles peuvent toujours être trouvés à partir de la console.
Kokodoko
L'erreur TypeScript se produit-elle lors du transfert de TypeScript vers JavaScript? (Comme la vérification de type se produit au moment de la transition. Plutôt qu'un mécanisme privé d'exécution.)
Eljay
4

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» processet processCallbacks. 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.

const Subscribable = (function(){

  const process = (self, eventName, args) => {
    self.processing.set(eventName, setTimeout(() => processCallbacks(self, eventName, args)))};

  const processCallbacks = (self, eventName, args) => {
    if (self.callingBack.get(eventName).length > 0){
      const [nextCallback, ...callingBack] = self.callingBack.get(eventName);
      self.callingBack.set(eventName, callingBack);
      process(self, eventName, args);
      nextCallback(...args)}
    else {
      delete self.processing.delete(eventName)}};

  return class {
    constructor(){
      this.callingBack = new Map();
      this.processing = new Map();
      this.toCallbacks = new Map()}

    subscribe(eventName, callback){
      const callbacks = this.unsubscribe(eventName, callback);
      this.toCallbacks.set(eventName,  [...callbacks, callback]);
      return () => this.unsubscribe(eventName, callback)}  // callable to unsubscribe for convenience

    unsubscribe(eventName, callback){
      let callbacks = this.toCallbacks.get(eventName) || [];
      callbacks = callbacks.filter(subscribedCallback => subscribedCallback !== callback);
      if (callbacks.length > 0) {
        this.toCallbacks.set(eventName, callbacks)}
      else {
        this.toCallbacks.delete(eventName)}
      return callbacks}

    emit(eventName, ...args){
      this.callingBack.set(eventName, this.toCallbacks.get(eventName) || []);
      if (!this.processing.has(eventName)){
        process(this, eventName, args)}}}})();

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é.

Paul Whipp
la source
4

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 unique Symbol, si vous n'avez pas déjà une autre propriété unique comme un id). Ensuite, gardez simplement une carte des variables «privées» de chaque objet en utilisant cet identifiant.

const privateVars = {};

class Something {
    constructor(){
        Object.defineProperty(this, '_sym', {
            configurable: false,
            enumerable: false,
            writable: false,
            value: Symbol()
        });

        var myPrivateVars = {
            privateProperty: "I'm hidden"
        };

        privateVars[this._sym] = myPrivateVars;

        this.property = "I'm public";
    }

    getPrivateProperty() {
        return privateVars[this._sym].privateProperty;
    }

    // A clean up method of some kind is necessary since the
    // variables won't be cleaned up from memory automatically
    // when the object is garbage collected
    destroy() {
        delete privateVars[this._sym];
    }
}

var instance = new Something();
console.log(instance.property); //=> "I'm public"
console.log(instance.privateProperty); //=> undefined
console.log(instance.getPrivateProperty()); //=> "I'm hidden"

L'avantage potentiel de cette approche par rapport à l'utilisation d'un WeakMapest un temps d'accès plus rapide si les performances deviennent un problème.

NanoWizard
la source
1
Corrigez-moi si je me trompe, mais ce code ne contiendrait-il pas des fuites de mémoire puisque privateVars stockera toujours les variables privées d'un objet même si l'objet est déjà détruit?
Russell Santos
@RussellSantos vous avez raison, en supposant que les objets devront être récupérés à un moment donné. Merci d'avoir fait remarquer cela. Dans mon exemple, j'ai ajouté une destroy()méthode qui devrait être appelée par le code using chaque fois qu'un objet doit être supprimé.
NanoWizard
4

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 ici

class Clazz {
  constructor() {
    var _level = 1

    function _private(x) {
      return _level * x;
    }
    return {
      level: _level,
      public: this.private,
      public2: function(x) {
        return _private(x);
      },
      public3: function(x) {
        return _private(x) * this.public(x);
      },
    };
  }

  private(x) {
    return x * x;
  }
}

var clazz = new Clazz();

console.log(clazz._level); //undefined
console.log(clazz._private); // undefined
console.log(clazz.level); // 1
console.log(clazz.public(1)); //1
console.log(clazz.public2(2)); //2
console.log(clazz.public3(3)); //27
console.log(clazz.private(0)); //error

1-14x0r
la source
3
class Something {
  constructor(){
    var _property = "test";
    Object.defineProperty(this, "property", {
        get: function(){ return _property}
    });
  }
}

var instance = new Something();
console.log(instance.property); //=> "test"
instance.property = "can read from outside, but can't write";
console.log(instance.property); //=> "test"
Ilya Zarembsky
la source
2
Il est préférable d'éviter les réponses codées uniquement. Ce serait mieux si vous pouviez expliquer comment votre code répond à la question du PO
Stewart_R
C'est vraiment comment faire une variable en lecture seule plus qu'une variable privée. Une variable privée ne doit pas être accessible à l'extérieur. console.log(instance.property)devrait jeter ou vous donner undefined, pas vous rendre "test".
oooyaya
3

Une autre façon similaire aux deux derniers publiés

class Example {
  constructor(foo) {

    // privates
    const self = this;
    this.foo = foo;

    // public interface
    return self.public;
  }

  public = {
    // empty data
    nodata: { data: [] },
    // noop
    noop: () => {},
  }

  // everything else private
  bar = 10
}

const test = new Example('FOO');
console.log(test.foo); // undefined
console.log(test.noop); // { data: [] }
console.log(test.bar); // undefined
Jayesbe
la source
2

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:

// 1. Create closure
var SomeClass = function() {
  // 2. Create `key` inside a closure
  var key = {};
  // Function to create private storage
  var private = function() {
    var obj = {};
    // return Function to access private storage using `key`
    return function(testkey) {
      if(key === testkey) return obj;
      // If `key` is wrong, then storage cannot be accessed
      console.error('Cannot access private properties');
      return undefined;
    };
  };
  var SomeClass = function() {
    // 3. Create private storage
    this._ = private();
    // 4. Access private storage using the `key`
    this._(key).priv_prop = 200;
  };
  SomeClass.prototype.test = function() {
    console.log(this._(key).priv_prop); // Using property from prototype
  };
  return SomeClass;
}();

// Can access private property from within prototype
var instance = new SomeClass();
instance.test(); // `200` logged

// Cannot access private property from outside of the closure
var wrong_key = {};
instance._(wrong_key); // undefined; error logged

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.

guitarino
la source
La question était de savoir comment y parvenir dans les classes ES6.
Michael Franzl
Vous pouvez utiliser exactement la même méthode dans les classes ES6. Les classes ES6 sont principalement du sucre en plus des fonctions comme je l'ai présenté dans mon exemple. Il est tout à fait possible que l'affiche originale utilise un transpilateur, auquel cas les WeakMaps ou les symboles nécessiteront toujours des polyfills. Ma réponse est valable malgré tout.
guitarino
2

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

kofifus
la source
2

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.

class Cat {
    constructor(name ,age) {
        this.name = name
        this.age = age
        Object.freeze(this)
    }
}

let cat = new Cat('Garfield', 5)
cat.age = 6 // doesn't work, even throws an error in strict mode
Nikola Andreev
la source
cela désactivera également la méthode de setName(name) { this.name = name; }
définition
2

J'utilise ce modèle et cela a toujours fonctionné pour moi

class Test {
    constructor(data) {
        class Public {
            constructor(prv) {

                // public function (must be in constructor on order to access "prv" variable)
                connectToDb(ip) {
                    prv._db(ip, prv._err);
                } 
            }

            // public function w/o access to "prv" variable
            log() {
                console.log("I'm logging");
            }
        }

        // private variables
        this._data = data;
        this._err = function(ip) {
            console.log("could not connect to "+ip);
        }
    }

    // private function
    _db(ip, err) {
        if(!!ip) {
		    console.log("connected to "+ip+", sending data '"+this.data+"'");
			return true;
		}
        else err(ip);
    }
}



var test = new Test(10),
		ip = "185.167.210.49";
test.connectToDb(ip); // true
test.log(); // I'm logging
test._err(ip); // undefined
test._db(ip, function() { console.log("You have got hacked!"); }); // undefined

Yami Teru
la source
2

En fait , il est possible.
1. D'abord, créez la classe et dans le constructeur retournez la _publicfonction appelée .
2. Dans la _publicfonction appelée , passez la thisréférence (pour obtenir l'accès à toutes les méthodes et accessoires privés) , et tous les arguments de constructor (qui seront passés new Names()).
3. Dans l' _publicétendue de la fonction, il y a aussi la Namesclasse avec l'accès à this(_this ) référence de la Namesclasse privée

class Names {
  constructor() {
    this.privateProperty = 'John';
    return _public(this, arguments);
  }
  privateMethod() { }
}

const names = new Names(1,2,3);
console.log(names.somePublicMethod); //[Function]
console.log(names.publicProperty); //'Jasmine'
console.log(names.privateMethod); //undefined
console.log(names.privateProperty); //undefind

function _public(_this, _arguments) {
  class Names {
    constructor() {
      this.publicProperty = 'Jasmine';
      _this.privateProperty; //"John";
      _this.privateMethod; //[Function]
    }

    somePublicMethod() {
      _this.privateProperty; //"John";
      _this.privateMethod; //[Function]
    }

  }
  return new Names(..._arguments);
}
Paweł
la source
2

Vous pouvez essayer ceci https://www.npmjs.com/package/private-members

Ce package enregistrera les membres par instance.

const pvt = require('private-members');
const _ = pvt();

let Exemplo = (function () {    
    function Exemplo() {
        _(this).msg = "Minha Mensagem";
    }

    _().mensagem = function() {
        return _(this).msg;
    }

    Exemplo.prototype.showMsg = function () {
        let msg = _(this).mensagem();
        console.log(msg);
    };

    return Exemplo;
})();

module.exports = Exemplo;
João Henrique
la source