Question

Eh bien, j'ai essayé de comprendre si cela était possible de toutes les manières. Voici le code:

a=function(text)
{
   var b=text;
   if (!arguments.callee.prototype.get)
      arguments.callee.prototype.get=function()
    {
         return b;
    }
    else
      alert('already created!');
}

var c=new a("test");  // creates prototype instance of getter
var d=new a("ojoj");  // alerts already created
alert(c.get())        // alerts test 
alert(d.get())        // alerts test from context of creating prototype function :(

Comme vous le voyez, j’ai essayé de créer un prototype de getter. Pour quoi? Eh bien, si vous écrivez quelque chose comme ceci:

a=function(text)
{
    var b=text;
    this.getText=function(){ return b}
}

... tout devrait bien se passer .. mais en fait, chaque fois que je crée un objet - je crée une fonction getText qui utilise la mémoire. Je voudrais avoir une fonction prototypique en mémoire qui ferait de même ... Des idées?

EDIT:

J'ai essayé la solution donnée par Christoph, et il semble que ce soit sa seule solution connue pour le moment. Il faut se souvenir des informations d’identification pour extraire la valeur du contexte, mais l’idée générale m’est agréable :) L’identité n’est qu’une chose à retenir, tout le reste pouvant être stocké une fois en mémoire. En fait, vous pouvez stocker de nombreux membres privés de cette manière et utiliser à tout moment un seul identifiant. En fait, cela me satisfait :) (à moins que quelqu'un en ait une meilleure idée).

someFunc = function()
{
  var store = new Array();
  var guid=0;
  var someFunc = function(text)
  {
    this.__guid=guid;
    store[guid++]=text;
  }

  someFunc.prototype.getValue=function()
  {
    return store[this.__guid];
  }

  return someFunc;
}()

a=new someFunc("test");
b=new someFunc("test2");

alert(a.getValue());
alert(b.getValue());
Était-ce utile?

La solution

Traditionnellement, JavaScript ne fournissait pas de mécanisme pour masquer les propriétés ("membres privés").

Étant donné que JavaScript a une portée lexicale, vous pouvez toujours le simuler au niveau de chaque objet en utilisant la fonction constructeur en tant que fermeture sur vos "membres privés" et en définissant vos méthodes dans le constructeur, mais cela ne fonctionnera pas pour les méthodes. défini dans la propriété prototype du constructeur.

Bien sûr, il existe des moyens de contourner ce problème, mais je ne le recommanderais pas:

Foo = (function() {
    var store = {}, guid = 0;

    function Foo() {
        this.__guid = ++guid;
        store[guid] = { bar : 'baz' };
    }

    Foo.prototype.getBar = function() {
        var privates = store[this.__guid];
        return privates.bar;
    };

    Foo.prototype.destroy = function() {
        delete store[this.__guid];
    };

    return Foo;
})();

Ceci stockera les propriétés "privées" dans un autre objet séparé de votre instance Foo. Assurez-vous d'appeler destroy() une fois que vous avez terminé avec l'objet: sinon, vous venez de créer une fuite de mémoire.

edit 2015-12-01: ECMAScript6 permet de réaliser des variantes améliorées ne nécessitant pas de destruction manuelle des objets, par exemple, à l'aide de WeakMap ou de préférence un Symbol , évitant ainsi la nécessité d’un magasin externe:

var Foo = (function() {
    var bar = Symbol('bar');

    function Foo() {
        this[bar] = 'baz';
    }

    Foo.prototype.getBar = function() {
        return this[bar];
    };

    return Foo;
})();

Autres conseils

Les méthodes du prototype ne peuvent pas accéder à " private " les membres tels qu'ils existent en javascript; vous avez besoin d’une sorte d’accesseur privilégié. Puisque vous déclarez get où il peut voir lexicalement b, il retournera toujours ce que <=> était lors de la création.

Après avoir été extrêmement inspiré par les travaux de Christoph, j’ai proposé un concept légèrement modifié qui apporte quelques améliorations. Encore une fois, cette solution est intéressante, mais pas nécessairement recommandée. Ces améliorations incluent:

  • Il n'est plus nécessaire d'effectuer aucune configuration dans le constructeur
  • Suppression de la nécessité de stocker un GUID public sur les instances
  • Ajout de quelques sucres syntaxiques

L’essentiel ici est d’utiliser l’objet d’instance lui-même comme clé pour accéder à l’objet privé associé. Normalement, cela n’est pas possible avec des objets simples car leurs clés doivent être des chaînes. Cependant, j'ai pu accomplir cela en utilisant le fait que l'expression ({} === {}) renvoie false. En d’autres termes, l’opérateur de comparaison peut distinguer entre des instances d’objets uniques.

En résumé, nous pouvons utiliser deux tableaux pour gérer les instances et leurs objets privés associés:

Foo = (function() {
    var instances = [], privates = [];

    // private object accessor function
    function _(instance) {
        var index = instances.indexOf(instance), privateObj;

        if(index == -1) {
            // Lazily associate instance with a new private object
            instances.push(instance);
            privates.push(privateObj = {});
        }
        else {
            // A privateObject has already been created, so grab that
            privateObj = privates[index];
        }
        return privateObj;
    }

    function Foo() {
        _(this).bar = "This is a private bar!";
    }

    Foo.prototype.getBar = function() {
        return _(this).bar;
    };

    return Foo;
})();

Vous remarquerez la _ fonction ci-dessus. C’est la fonction d’accesseur pour saisir l’objet privé. Cela fonctionne paresseusement, donc si vous l'appelez avec une nouvelle instance, cela créera un nouvel objet privé à la volée.

Si vous ne souhaitez pas dupliquer le <=> code pour chaque classe, vous pouvez résoudre ce problème en l'enveloppant dans une fonction fabrique:

function createPrivateStore() {
    var instances = [], privates = [];

    return function (instance) {
        // Same implementation as example above ...
    };
}

Vous pouvez maintenant le réduire à une seule ligne pour chaque classe:

var _ = createPrivateStore();

Encore une fois, vous devez faire très attention en utilisant cette solution car elle peut créer des fuites de mémoire si vous n'implémentez pas et n'appelez pas de fonction de destruction si nécessaire.

Avec les navigateurs modernes adoptant certaines technologies ES6, vous pouvez utiliser WeakMap pour contourner le problème du GUID. Cela fonctionne dans IE11 et les versions ultérieures:

// Scope private vars inside an IIFE
var Foo = (function() { 
    // Store all the Foos, and garbage-collect them automatically
    var fooMap = new WeakMap();

    var Foo = function(txt) { 
        var privateMethod = function() { 
            console.log(txt); 
        };
        // Store this Foo in the WeakMap
        fooMap.set(this, {privateMethod: privateMethod}); 
    } 

    Foo.prototype = Object.create(Object.prototype); 
    Foo.prototype.public = function() { 
        fooMap.get(this).p(); 
     } 
     return Foo; 
 }());

 var foo1 = new Foo("This is foo1's private method");
 var foo2 = new Foo("This is foo2's private method");
 foo1.public(); // "This is foo1's private method"
 foo2.public(); // "This is foo2's private method"

Foo ne stockera de références à aucune <=> une fois supprimés ou supprimés, et comme il utilise des objets comme clés, vous n'avez pas besoin de joindre des GUID à votre objet.

Personnellement, je n’aime pas beaucoup la solution avec guid, car elle oblige le développeur à la déclarer en plus du magasin et à l’incrémenter dans le constructeur. En gros, les développeurs d’applications javascript peuvent oublier de le faire, ce qui est très sujet aux erreurs.

J'aime beaucoup la réponse de Peter car vous pouvez accéder aux membres privés en utilisant le contexte (this). Mais une chose qui me dérange beaucoup est le fait que l'accès aux députés est fait dans une complexité énorme. En effet, trouver l'indice d'un objet dans un tableau est un algorithme linéaire. Considérez que vous souhaitez utiliser ce modèle pour un objet instancié 10000 fois. Ensuite, vous pouvez parcourir 10000 instances chaque fois que vous souhaitez accéder à un membre privé.

Pour accéder aux magasins privés dans une complexité o (1), il n’ya pas d’autre moyen que d’utiliser des guids. Mais pour ne pas déranger avec la déclaration guid et l’incrémentation et pour utiliser le contexte afin d’accéder au magasin privé, j’ai modifié le modèle d’usine Peters comme suit:

createPrivateStore = function () {
var privates = {}, guid = 0;

return function (instance) {
    if (instance.__ajxguid__ === undefined) {
        // Lazily associate instance with a new private object
        var private_obj = {};
        instance.__ajxguid__ = ++guid;
        privates[instance.__ajxguid__] = private_obj;
        return private_obj;
    }

    return privates[instance.__ajxguid__];
}

}

Le truc ici consiste à considérer que les objets qui ne possèdent pas la propriété ajxguid ne sont pas encore gérés. En effet, on pourrait définir manuellement la propriété avant d’accéder au magasin pour la première fois, mais je pense qu’il n’existe pas de solution magique.

Je pense que la vraie vie privée est surestimée. La confidentialité virtuelle est tout ce qui est nécessaire. Je pense que l'utilisation de _privateIdentifier est un pas dans la bonne direction, mais pas assez, car on vous présente toujours une liste de tous les _privateIdentifiers dans les popups intellisense. Une autre et meilleure étape consiste à créer un objet dans la fonction prototype et / ou constructeur pour séparer vos champs et méthodes pratiquement privés de la vue, comme suit:

  // Create the object
  function MyObject() {}

  // Add methods to the prototype
  MyObject.prototype = {

    // This is our public method
    public: function () {
      console.log('PUBLIC method has been called');
    },

    // This is our private method tucked away inside a nested privacy object called x
    x: {
      private: function () {
        console.log('PRIVATE method has been called');
      }
    },

  }

// Create an instance of the object
var mo = new MyObject(); 

maintenant quand le codeur tape & "; mo. &"; intellisense ne montrera que la fonction publique et & "; x &"; Tous les membres privés ne sont donc pas affichés mais cachés derrière & "; X &"; il est donc plus improbable qu'un codeur appelle accidentellement un membre privé, car il devra sortir de son chemin et taper & "; mo.x. &". pour voir des membres privés. Cette méthode évite également que la liste intellisense soit encombrée de plusieurs noms de membres privés, en les cachant tous derrière l'élément unique & "; X &";.

.

Je sais que ce fil est vraiment très vieux maintenant, mais je pense que cette solution pourrait intéresser tous les passants:

const Immutable = function ( val ) {
    let _val = val;

    this.$ = {
        _resolve: function () {
            return _val;
        }
    };
};
Immutable.prototype = {
    resolve: function () {
        return this.$._resolve();
    }
};

Cacher essentiellement le _val interne pour qu’il ne soit pas manipulé et rendre une instance de cet objet immuable.

J'ai créé une nouvelle bibliothèque pour activer les méthodes privées sur la chaîne de prototypes. https://github.com/TremayneChrist/ProtectJS

Exemple:

var MyObject = (function () {

  // Create the object
  function MyObject() {}

  // Add methods to the prototype
  MyObject.prototype = {

    // This is our public method
    public: function () {
      console.log('PUBLIC method has been called');
    },

    // This is our private method, using (_)
    _private: function () {
      console.log('PRIVATE method has been called');
    }
  }

  return protect(MyObject);

})();

// Create an instance of the object
var mo = new MyObject();

// Call its methods
mo.public(); // Pass
mo._private(); // Fail
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top