web-dev-qa-db-fra.com

Pourquoi puis-je accéder aux membres privés TypeScript alors que je ne devrais pas pouvoir le faire?

Je regarde l'implémentation de membres privés dans TypeScript, et je trouve cela un peu déroutant. Intellisense ne permet pas d'accéder à un membre privé, mais en JavaScript pur, tout y est. Cela me fait penser que TS n'implémente pas correctement les membres privés. Des pensées?

class Test{
  private member: any = "private member";
}
alert(new Test().member);
98
Sean Feldman

Comme pour la vérification de type, la confidentialité des membres est uniquement appliquée au sein du compilateur.

Une propriété privée est implémentée en tant que propriété standard et le code en dehors de la classe n'est pas autorisé à y accéder.

Pour rendre quelque chose de vraiment privé dans la classe, il ne peut s'agir d'un membre de la classe, mais d'une variable locale créée dans une portée de fonction dans le code qui crée l'objet. Cela signifierait que vous ne pouvez pas y accéder comme un membre de la classe, c’est-à-dire en utilisant le mot-clé this.

89
Guffa

JavaScript supporte les variables privées.

function MyClass() {
    var myPrivateVar = 3;

    this.doSomething = function() {
        return myPrivateVar++;        
    }
}

Dans TypeScript, cela serait exprimé ainsi:

class MyClass {

    doSomething: () => number;

    constructor() {
        var myPrivateVar = 3;

        this.doSomething = function () {
            return myPrivateVar++;
        }
    }
}

EDIT

Cette approche ne devrait être utilisée que AVEC PARCIMONIE où elle est absolument nécessaire. Par exemple, si vous devez mettre en cache un mot de passe temporairement.

L'utilisation de ce modèle entraîne des coûts de performance (non liés à Javascript ou à TypeScript) et ne doit être utilisé qu'en cas d'absolue nécessité.

36
Martin

Une fois que le support de WeakMap est plus largement disponible, il existe une technique intéressante détaillée dans l'exemple n ° 3 ici .

Il permet de stocker des données privées ET évite les coûts de performance de Jason Evans en rendant les données accessibles à partir de méthodes prototypes au lieu de méthodes à instance unique.

La page liée MDN WeakMap indique la prise en charge du navigateur à Chrome 36, Firefox 6.0, IE 11, Opera 23 et Safari 7.1.

let _counter = new WeakMap();
let _action = new WeakMap();
class Countdown {
  constructor(counter, action) {
    _counter.set(this, counter);
    _action.set(this, action);
  }
  decrement() {
    let counter = _counter.get(this);
    if (counter < 1) return;
    counter--;
    _counter.set(this, counter);
    if (counter === 0) {
      _action.get(this)();
    }
  }
}
12
Ryan Thomas

Merci à Sean Feldman pour le lien vers la discussion officielle sur cette question - voir sa réponse pour le lien.

J'ai lu la discussion à laquelle il a lié, et voici un résumé des points clés:

  • Suggestion: propriétés privées dans le constructeur
    • problèmes: Impossible d'accéder aux fonctions prototypes
  • Suggestion: méthodes privées dans le constructeur
    • problèmes: comme avec les propriétés, vous perdez l'avantage de performance de créer une fonction une fois par classe dans le prototype; à la place, vous créez une copie de la fonction pour chaque instance
  • Suggestion: ajouter un passe-partout pour accéder aux propriétés abstraites et appliquer la visibilité
    • problèmes: surcharge de performance; TypeScript est conçu pour les grandes applications
  • Suggestion: TypeScript encapsule déjà les définitions de constructeur et de méthode prototype dans une fermeture; mettre les méthodes et propriétés privées ici
    • des problèmes pour mettre des propriétés privées dans cette fermeture: elles deviennent des variables statiques; il n'y en a pas un par exemple
    • problèmes avec la mise en place de méthodes privées dans cette fermeture: ils n'ont pas accès à this sans solution de contournement
  • Suggestion: Mangle automatiquement les noms de variables privées
    • arguments du compteur: c'est une convention de nommage, pas une construction de langage. Mangle toi-même
  • Suggestion: Annotez les méthodes privées avec @private donc les minificateurs qui reconnaissent que l’annotation peuvent effectivement réduire les noms de méthodes
    • Aucun contre-argument significatif à celui-ci

Contre-arguments globaux pour l'ajout de la prise en charge de la visibilité dans le code émis:

  • le problème est que JavaScript lui-même n'a pas de modificateurs de visibilité - ce n'est pas le problème de TypeScript
  • il existe déjà un modèle établi dans la communauté JavaScript: préfixez les propriétés et méthodes privées avec un trait de soulignement, qui indique "procédez à vos risques et périls"
  • lorsque les concepteurs TypeScript ont déclaré que les propriétés et méthodes réellement privées ne sont pas "possibles", elles signifiaient "impossible sous nos contraintes de conception", en particulier:
    • Le JS émis est idiomatique
    • La plaque de cuisson est minimale
    • Pas de frais généraux supplémentaires par rapport à la JS OOP normale
3
alexanderbird

Dans TypeScript, les fonctions privées ne sont accessibles qu'à l'intérieur de la classe. Comme

enter image description here

Et il y aura une erreur lorsque vous essayez d'accéder à un membre privé. Voici l'exemple:

enter image description here

Remarque: avec javascript, les deux fonctions sont accessibles à l'extérieur.

2
Muhammad Awais

Je réalise que cette discussion est plus ancienne, mais il pourrait toujours être utile de partager ma solution au problème des variables et méthodes supposément privées dans un TypeScript "filtrant" dans l'interface publique de la classe JavaScript compilée.

Pour moi, ce problème est purement esthétique, c’est-à-dire qu’il s’agit de l’encombrement visuel lorsqu’une variable d’instance est affichée dans DevTools. Ma solution consiste à regrouper les déclarations privées dans une autre classe, qui est ensuite instanciée dans la classe principale et affectée à une variable private (mais toujours visible publiquement dans JS) avec un nom comme __ (double trait de soulignement).

Exemple:

class Privates {
    readonly DEFAULT_MULTIPLIER = 2;
    foo: number;
    bar: number;

    someMethod = (multiplier: number = this.DEFAULT_MULTIPLIER) => {
        return multiplier * (this.foo + this.bar);
    }

    private _class: MyClass;

    constructor(_class: MyClass) {
        this._class = _class;
    }
}

export class MyClass {
    private __: Privates = new Privates(this);

    constructor(foo: number, bar: number, baz: number) {
        // assign private property values...
        this.__.foo = foo;
        this.__.bar = bar;

        // assign public property values...
        this.baz = baz;
    }

    baz: number;

    print = () => {
        console.log(`foo=${this.__.foo}, bar=${this.__.bar}`);
        console.log(`someMethod returns ${this.__.someMethod()}`);
    }
}

let myClass = new MyClass(1, 2, 3);

Lorsque l'instance myClass est affichée dans DevTools, au lieu de voir tous ses membres "privés" entremêlés avec des membres véritablement publics (qui peuvent être très visuellement désordonnés dans du code réel refactorisé correctement), vous les voyez parfaitement regroupés à l'intérieur du code. s'est effondré __ propriété:

enter image description here

1
Caspian Canuck