JavaScript Gestion de données privées avec des classes


Exemple

L'un des obstacles les plus courants à l'utilisation des classes est de trouver la bonne approche pour gérer les états privés. Il existe 4 solutions courantes pour gérer les états privés:

Utiliser des symboles

Les symboles sont un nouveau type primitif introduit dans ES2015, tel que défini dans MDN

Un symbole est un type de données unique et immuable qui peut être utilisé comme identifiant pour les propriétés de l'objet.

Lorsque vous utilisez le symbole comme clé de propriété, il n'est pas énumérable.

En tant que tels, ils ne seront pas révélés en utilisant for var in ou Object.keys .

Ainsi, nous pouvons utiliser des symboles pour stocker des données privées.

const topSecret = Symbol('topSecret'); // our private key; will only be accessible on the scope of the module file
export class SecretAgent{
    constructor(secret){
        this[topSecret] = secret; // we have access to the symbol key (closure)
        this.coverStory = 'just a simple gardner';
        this.doMission = () => {
            figureWhatToDo(topSecret[topSecret]); // we have access to topSecret
        };
    }
}

Comme les symbols sont uniques, nous devons nous référer au symbole d'origine pour accéder à la propriété privée.

import {SecretAgent} from 'SecretAgent.js'
const agent = new SecretAgent('steal all the ice cream');
// ok lets try to get the secret out of him!
Object.keys(agent); // ['coverStory'] only cover story is public, our secret is kept.
agent[Symbol('topSecret')]; // undefined, as we said, symbols are always unique, so only the original symbol will help us to get the data.

Mais ce n'est pas 100% privé; brisons cet agent! Nous pouvons utiliser la méthode Object.getOwnPropertySymbols pour obtenir les symboles d'objet.

const secretKeys = Object.getOwnPropertySymbols(agent);
agent[secretKeys[0]] // 'steal all the ice cream' , we got the secret.

Utiliser WeakMaps

WeakMap est un nouveau type d'objet qui a été ajouté pour es6.

Comme défini sur MDN

L'objet WeakMap est un ensemble de paires clé / valeur dans lesquelles les clés sont faiblement référencées. Les clés doivent être des objets et les valeurs peuvent être des valeurs arbitraires.

Une autre caractéristique importante de WeakMap est la définition de MDN .

La clé dans un WeakMap est maintenue faiblement. Cela signifie que, s’il n’ya pas d’autres références fortes à la clé, l’entrée entière sera supprimée de WeakMap par le garbage collector.

L'idée est d'utiliser le WeakMap, en tant que carte statique pour toute la classe, pour contenir chaque instance en tant que clé et conserver les données privées en tant que valeur pour cette clé d'instance.

Ainsi, seulement dans la classe, nous aurons accès à la collection WeakMap .

Essayons notre agent avec WeakMap :

const topSecret = new WeakMap(); // will hold all private data of all instances.
export class SecretAgent{
    constructor(secret){
        topSecret.set(this,secret); // we use this, as the key, to set it on our instance private data
        this.coverStory = 'just a simple gardner';
        this.doMission = () => {
            figureWhatToDo(topSecret.get(this)); // we have access to topSecret
        };
    }
}

Puisque le const topSecret est défini dans notre fermeture de module et que nous ne l'avons pas lié à nos propriétés d'instance, cette approche est totalement privée et nous ne pouvons pas atteindre l'agent topSecret .

Définir toutes les méthodes à l'intérieur du constructeur

L'idée ici est simplement de définir toutes nos méthodes et membres à l' intérieur du constructeur et utiliser la fermeture pour accéder aux membres privés sans les affecter à this .

   export class SecretAgent{
        constructor(secret){
            const topSecret = secret;
            this.coverStory = 'just a simple gardner';
            this.doMission = () => {
                figureWhatToDo(topSecret); // we have access to topSecret
            };
        }
    }

Dans cet exemple également, les données sont 100% privées et ne peuvent pas être atteintes en dehors de la classe. Notre agent est donc sécurisé.

Utilisation des conventions de dénomination

Nous déciderons que toute propriété privée sera préfixée par _ .

Notez que pour cette approche, les données ne sont pas vraiment privées.

export class SecretAgent{
    constructor(secret){
        this._topSecret = secret; // it private by convention
        this.coverStory = 'just a simple gardner';
        this.doMission = () => {
            figureWhatToDo(this_topSecret); 
        };
    }
}