web-dev-qa-db-fra.com

Comment implémenter une méthode privée en classe ES6 avec Traceur

J'utilise Traceur Compiler pour tirer parti des fonctionnalités de l'ES6.

Je veux implémenter ce genre de choses depuis ES5:

function Animal() {
    var self = this,
        sayHi;

    sayHi  = function() {
        self.hi();
    };

    this.hi = function() {/* ... */}
}

Actuellement, traceur ne prend pas en charge les mots-clés private et public ( d'harmonie ). Et la syntaxe de classe ES6 ne permet pas d'utiliser des instructions simples var (ou let) dans le corps de la classe.

Le seul moyen que je trouve est de simuler des soldats avant la déclaration de classe. Quelque chose comme:

var sayHi = function() {
    // ... do stuff
};

class Animal {
...

Il vaut mieux que rien, mais comme prévu, vous ne pouvez pas passer la bonne this à une méthode privée sans apply- ing ou bind- à chaque fois.

Alors, est-il possible d'utiliser des données privées de classe ES6 compatibles avec le compilateur traceur?

131
Glen Swift

Il n'y a pas de mots-clés private, public ou protected dans la version actuelle spécification ECMAScript 6 .

Donc, Traceur ne supporte pas private et public. 6to5 (actuellement appelé "Babel") réalise cette proposition à des fins expérimentales (voir aussi cette discussion ). Mais ce n'est qu'une proposition, après tout.

Donc, pour l'instant, vous pouvez simplement simuler des propriétés privées via WeakMap (voir ici ). Une autre alternative est Symbol - mais elle ne fournit pas de confidentialité, car la propriété est facilement accessible via Object.getOwnPropertySymbols.

IMHO la meilleure solution pour le moment - utilisez simplement la pseudo-confidentialité. Si vous utilisez fréquemment apply ou call avec votre méthode, cette méthode est très spécifique à un objet. Donc, ça vaut la peine de le déclarer dans votre classe juste avec le préfixe de soulignement:

class Animal {

    _sayHi() {
        // do stuff
    }
}
208
alexpods

Vous pouvez toujours utiliser les fonctions normales:

function myPrivateFunction() {
  console.log("My property: " + this.prop);
}

class MyClass() {
  constructor() {
    this.prop = "myProp";
    myPrivateFunction.bind(this)();
  }
}

new MyClass(); // 'My property: myProp'
67
Max

Bien qu’il n’existe actuellement aucun moyen de déclarer une méthode ou une propriété comme privée, les modules ES6 ne sont pas dans l’espace de noms global. Par conséquent, tout ce que vous déclarez dans votre module et que vous n'exportez pas ne sera disponible pour aucune autre partie de votre programme, mais sera toujours disponible pour votre module pendant l'exécution. Ainsi, vous avez des propriétés et des méthodes privées :)

Voici un exemple (dans le fichier test.js])

function tryMe1(a) {
  console.log(a + 2);
}

var tryMe2 = 1234;

class myModule {
  tryMe3(a) {
    console.log(a + 100);
  }

  getTryMe1(a) {
    tryMe1(a);
  }

  getTryMe2() {
    return tryMe2;
  }
}

// Exports just myModule class. Not anything outside of it.
export default myModule; 

Dans un autre fichier

import MyModule from './test';

let bar = new MyModule();

tryMe1(1); // ReferenceError: tryMe1 is not defined
tryMe2; // ReferenceError: tryMe2 is not defined
bar.tryMe1(1); // TypeError: bar.tryMe1 is not a function
bar.tryMe2; // undefined

bar.tryMe3(1); // 101
bar.getTryMe1(1); // 3
bar.getTryMe2(); // 1234
53
Marcelo Lazaroni

Vous pouvez utiliser le symbole

var say = Symbol()

function Cat(){
  this[say]() // call private methos
}

Cat.prototype[say] = function(){ alert('im a private') }

P.S. alexpods n'est pas correct. il est protégé plutôt que privé, car l'héritage est un conflit de noms

En fait, vous pouvez utiliser var say = String(Math.random()) à la place de Symbol

EN ES6:

var say = Symbol()

class Cat {

  constructor(){
    this[say]() // call private
  }

  [say](){
    alert('im private')
  }

}
22
Maxmaxmaximus

J'espère que cela peut être utile. :)

I. Déclarer vars, fonctions inside IIFE (expression de fonction invoquée immédiatement) , celles-ci ne peuvent être utilisées que dans la fonction anonyme. (Il peut être intéressant d’utiliser des mots-clés "let, const" sans utiliser "var" lorsque vous devez modifier le code de ES6.)

let Name = (function() {
  const _privateHello = function() {
  }
  class Name {
    constructor() {
    }
    publicMethod() {
      _privateHello()
    }
  }
  return Name;
})();

II. L'objet WeakMap peut être utile pour des problèmes de mémoire vive.

Les variables stockées dans WeakMap seront supprimées lorsque l'instance sera supprimée. Vérifiez cet article. ( Gestion des données privées des classes ES6 )

let Name = (function() {
  const _privateName = new WeakMap();
})();

III. Mettons tout ensemble.

let Name = (function() {
  const _privateName = new WeakMap();
  const _privateHello = function(fullName) {
    console.log("Hello, " + fullName);
  }

  class Name {
    constructor(firstName, lastName) {
      _privateName.set(this, {firstName: firstName, lastName: lastName});
    }
    static printName(name) {
      let privateName = _privateName.get(name);
      let _fullname = privateName.firstName + " " + privateName.lastName;
      _privateHello(_fullname);
    }
    printName() {
      let privateName = _privateName.get(this);
      let _fullname = privateName.firstName + " " + privateName.lastName;
      _privateHello(_fullname);
    }
  }

  return Name;
})();

var aMan = new Name("JH", "Son");
aMan.printName(); // "Hello, JH Son"
Name.printName(aMan); // "Hello, JH Son"
12
Son JoungHo

Comme le dit alexpods, il n’existe aucun moyen de le faire dans ES6. Cependant, pour ceux que cela intéresse, il existe également une proposition pour opérateur de liaison qui permet ce type de syntaxe:

function privateMethod() {
  return `Hello ${this.name}`;
}

export class Animal {
  constructor(name) {
    this.name = name;
  }
  publicMethod() {
    this::privateMethod();
  }
}

Encore une fois, il ne s'agit que d'une proposition. Votre kilométrage peut varier.

9
jamsesso

Avez-vous envisagé d'utiliser les fonctions d'usine? Ils sont généralement un bien meilleure alternative aux classes ou aux fonctions constructeurs en Javascript. Voici un exemple de la façon dont cela fonctionne:

function car () {

    var privateVariable = 4

    function privateFunction () {}

    return {

        color: 'red',

        drive: function (miles) {},

        stop: function() {}

        ....

    }

}

Grâce aux fermetures, vous avez accès à toutes les fonctions et variabels privées à l'intérieur de l'objet renvoyé, mais vous ne pouvez pas y accéder de l'extérieur.

5
Nicola Pedretti

Comme Marcelo Lazaroni a déjà dit,

Bien qu’il n’existe actuellement aucun moyen de déclarer une méthode ou une propriété comme étant privée, les modules ES6 ne figurent pas dans l’espace de noms global. Par conséquent, tout ce que vous déclarez dans votre module et que vous n'exportez pas ne sera disponible pour aucune autre partie de votre programme, mais le sera toujours pendant l'exécution.

Mais son exemple n'a pas montré comment la méthode privée pouvait accéder aux membres de l'instance de la classe. Max nous montre quelques bons exemples de la manière dont les membres de l'instance d'accès via une liaison ou l'utilisation alternative d'une méthode lambda dans le constructeur, mais j'aimerais ajouter un moyen plus simple de le faire: passer l'instance en tant que un paramètre à la méthode privée. En procédant ainsi, la classe MyClass de Max ressemblerait à ceci:

function myPrivateFunction(myClass) {
  console.log("My property: " + myClass.prop);
}

class MyClass() {
  constructor() {
    this.prop = "myProp";
  }
  testMethod() {
    myPrivateFunction(this);
  }
}
module.exports = MyClass;

La façon dont vous le faites dépend vraiment de vos préférences personnelles.

4
Patrick Graham

Je suis venu avec ce que je pense est une solution bien meilleure permettant:

  • pas besoin de 'this._', that/self, cartes faibles, symboles, etc. Code 'classe' clair et simple

  • les variables et méthodes privées sont vraiment privées et ont le bon lien 'this'

  • Aucune utilisation de 'this', ce qui signifie un code clair beaucoup moins sujet aux erreurs

  • l'interface publique est claire et séparée de l'implémentation en tant que proxy pour les méthodes privées

  • permet une composition facile

avec cela vous pouvez faire:

function Counter() {
  // public interface
  const proxy = {
    advance,  // advance counter and get new value
    reset,    // reset value
    value     // get value
  }
        
  // private variables and methods
  let count=0;
    
  function advance() {
    return ++count;
  }
        
  function reset(newCount) {
    count=(newCount || 0);
  }
        
  function value() {
    return count;
  }
    
  return proxy;
}
        
let counter=Counter.New();
console.log(counter instanceof Counter); // true
counter.reset(100);
console.log('Counter next = '+counter.advance()); // 101
console.log(Object.getOwnPropertyNames(counter)); // ["advance", "reset", "value"]
<script src="https://cdn.rawgit.com/kofifus/New/7987670c/new.js"></script>

voir Nouvea pour le code et des exemples plus élaborés, y compris constructeur et composition

2
kofifus