Accès aux variables de membre privé à partir de fonctions définies par prototype

188

Existe-t-il un moyen de rendre les variables «privées» (celles définies dans le constructeur), disponibles aux méthodes définies par prototype?

TestClass = function(){
    var privateField = "hello";
    this.nonProtoHello = function(){alert(privateField)};
};
TestClass.prototype.prototypeHello = function(){alert(privateField)};

Cela marche:

t.nonProtoHello()

Mais ce n'est pas le cas:

t.prototypeHello()

J'ai l'habitude de définir mes méthodes à l'intérieur du constructeur, mais je m'éloigne de cela pour plusieurs raisons.

Morgancodes
la source
14
@ecampver, Sauf que celle-ci a été posée 2 ans plus tôt ....
Pacerier

Réponses:

194

Non, il n'y a aucun moyen de le faire. Ce serait essentiellement une approche inverse.

Les méthodes définies dans le constructeur ont accès aux variables privées car toutes les fonctions ont accès à la portée dans laquelle elles ont été définies.

Les méthodes définies sur un prototype ne sont pas définies dans la portée du constructeur et n'auront pas accès aux variables locales du constructeur.

Vous pouvez toujours avoir des variables privées, mais si vous voulez que les méthodes définies sur le prototype y aient accès, vous devez définir des getters et des setters sur l' thisobjet, auxquels les méthodes du prototype (ainsi que tout le reste) auront accès. Par exemple:

function Person(name, secret) {
    // public
    this.name = name;

    // private
    var secret = secret;

    // public methods have access to private members
    this.setSecret = function(s) {
        secret = s;
    }

    this.getSecret = function() {
        return secret;
    }
}

// Must use getters/setters 
Person.prototype.spillSecret = function() { alert(this.getSecret()); };
Triptyque
la source
14
"scoping in reverse" est une fonctionnalité C ++ avec le mot clé "friend". En gros, toute fonction doit définir son prototype comme son ami. Malheureusement, ce concept est C ++ et non JS :(
TWiStErRob
1
Je voudrais ajouter ce post en haut de ma liste de favoris et le conserver.
Donato
2
Je ne vois pas l'intérêt de cela - vous n'ajoutez qu'une couche d'abstraction qui ne fait rien. Vous pouvez tout aussi bien faire secretune propriété this. JavaScript ne prend tout simplement pas en charge les variables privées avec les prototypes, car les prototypes sont liés au contexte du site d'appel, pas au contexte du «site de création».
nicodemus13
1
Pourquoi ne pas le faire person.getSecret()alors?
Fahmi
1
Pourquoi cela a-t-il autant de votes positifs? Cela ne rend pas la variable privée. Comme mentionné ci-dessus, l'utilisation de person.getSecret () vous permettra d'accéder à cette variable privée de n'importe où.
alexr101
64

Mise à jour: avec ES6, il existe un meilleur moyen:

Pour faire court, vous pouvez utiliser le nouveau Symbolpour créer des champs privés.
Voici une excellente description: https://curiosity-driven.org/private-properties-in-javascript

Exemple:

var Person = (function() {
    // Only Person can access nameSymbol
    var nameSymbol = Symbol('name');

    function Person(name) {
        this[nameSymbol] = name;
    }

    Person.prototype.getName = function() {
        return this[nameSymbol];
    };

    return Person;
}());

Pour tous les navigateurs modernes avec ES5:

Vous pouvez utiliser uniquement des fermetures

La façon la plus simple de construire des objets est d'éviter complètement l'héritage prototypique. Définissez simplement les variables privées et les fonctions publiques dans la fermeture, et toutes les méthodes publiques auront un accès privé aux variables.

Ou vous pouvez utiliser uniquement des prototypes

En JavaScript, l'héritage prototypique est avant tout une optimisation . Il permet à plusieurs instances de partager des méthodes prototypes, plutôt que chaque instance ayant ses propres méthodes.
L'inconvénient est que thisc'est la seule chose qui diffère à chaque fois qu'une fonction prototypique est appelée.
Par conséquent, tous les champs privés doivent être accessibles via this, ce qui signifie qu'ils seront publics. Nous nous en tenons donc simplement aux conventions de dénomination des _privatechamps.

Ne vous embêtez pas à mélanger des fermetures avec des prototypes

Je pense que vous ne devriez pas mélanger des variables de fermeture avec des méthodes prototypes. Vous devez utiliser l'un ou l'autre.

Lorsque vous utilisez une fermeture pour accéder à une variable privée, les méthodes de prototype ne peuvent pas accéder à la variable. Donc, vous devez exposer la fermeture this, ce qui signifie que vous l'exposez publiquement d'une manière ou d'une autre. Il y a très peu à gagner avec cette approche.

Lequel dois-je choisir?

Pour les objets vraiment simples, utilisez simplement un objet simple avec des fermetures.

Si vous avez besoin d'un héritage prototypique - pour l'héritage, les performances, etc. - respectez la convention de dénomination "_private" et ne vous souciez pas des fermetures.

Je ne comprends pas pourquoi les développeurs JS s'efforcent de rendre les champs vraiment privés.

Scott Rippey
la source
4
Malheureusement, la _privateconvention de dénomination reste la meilleure solution si vous souhaitez tirer parti de l'héritage prototypique.
écraser
1
ES6 aura un nouveau concept, le Symbol, qui est un excellent moyen de créer des champs privés. Voici une excellente explication: curiosity-driven.org/private-properties-in-javascript
Scott Rippey
1
Non, vous pouvez garder le Symboldans une fermeture qui englobe toute votre classe. De cette façon, toutes les méthodes de prototype peuvent utiliser le symbole, mais il n'est jamais exposé en dehors de la classe.
Scott Rippey
2
L'article que vous avez lié dit: "Les symboles sont similaires aux noms privés mais - contrairement aux noms privés - ils n'offrent pas une véritable confidentialité . ". En effet, si vous avez l'instance, vous pouvez obtenir ses symboles avec Object.getOwnPropertySymbols. Ce n'est donc que la confidentialité par l'obscurité.
Oriol
2
@Oriol Ouais, la confidentialité passe par une lourde obscurité. Il est toujours possible d'itérer à travers les symboles, et vous en indiquez le but via toString. Ce n'est pas différent de Java ou C # ... les membres privés sont toujours accessibles via la réflexion, mais sont généralement fortement masqués. Ce qui renforce mon dernier point: "Je ne comprends pas pourquoi les développeurs JS essaient si fort de rendre les champs vraiment privés."
Scott Rippey
31

Quand j'ai lu ceci, cela ressemblait à un défi difficile, alors j'ai décidé de trouver un moyen. Ce que j'ai trouvé, c'est CRAAAAZY mais ça marche totalement.

Tout d'abord, j'ai essayé de définir la classe dans une fonction immédiate afin que vous ayez accès à certaines des propriétés privées de cette fonction. Cela fonctionne et vous permet d'obtenir des données privées, cependant, si vous essayez de définir les données privées, vous constaterez bientôt que tous les objets partageront la même valeur.

var SharedPrivateClass = (function() { // use immediate function
    // our private data
    var private = "Default";

    // create the constructor
    function SharedPrivateClass() {}

    // add to the prototype
    SharedPrivateClass.prototype.getPrivate = function() {
        // It has access to private vars from the immediate function!
        return private;
    };

    SharedPrivateClass.prototype.setPrivate = function(value) {
        private = value;
    };

    return SharedPrivateClass;
})();

var a = new SharedPrivateClass();
console.log("a:", a.getPrivate()); // "a: Default"

var b = new SharedPrivateClass();
console.log("b:", b.getPrivate()); // "b: Default"

a.setPrivate("foo"); // a Sets private to "foo"
console.log("a:", a.getPrivate()); // "a: foo"
console.log("b:", b.getPrivate()); // oh no, b.getPrivate() is "foo"!

console.log(a.hasOwnProperty("getPrivate")); // false. belongs to the prototype
console.log(a.private); // undefined

// getPrivate() is only created once and instanceof still works
console.log(a.getPrivate === b.getPrivate);
console.log(a instanceof SharedPrivateClass);
console.log(b instanceof SharedPrivateClass);

Il y a de nombreux cas où cela serait adéquat, comme si vous vouliez avoir des valeurs constantes telles que les noms d'événements partagés entre les instances. Mais essentiellement, ils agissent comme des variables statiques privées.

Si vous avez absolument besoin d'accéder à des variables dans un espace de noms privé à partir de vos méthodes définies sur le prototype, vous pouvez essayer ce modèle.

var PrivateNamespaceClass = (function() { // immediate function
    var instance = 0, // counts the number of instances
        defaultName = "Default Name",  
        p = []; // an array of private objects

    // create the constructor
    function PrivateNamespaceClass() {
        // Increment the instance count and save it to the instance. 
        // This will become your key to your private space.
        this.i = instance++; 
        
        // Create a new object in the private space.
        p[this.i] = {};
        // Define properties or methods in the private space.
        p[this.i].name = defaultName;
        
        console.log("New instance " + this.i);        
    }

    PrivateNamespaceClass.prototype.getPrivateName = function() {
        // It has access to the private space and it's children!
        return p[this.i].name;
    };
    PrivateNamespaceClass.prototype.setPrivateName = function(value) {
        // Because you use the instance number assigned to the object (this.i)
        // as a key, the values set will not change in other instances.
        p[this.i].name = value;
        return "Set " + p[this.i].name;
    };

    return PrivateNamespaceClass;
})();

var a = new PrivateNamespaceClass();
console.log(a.getPrivateName()); // Default Name

var b = new PrivateNamespaceClass();
console.log(b.getPrivateName()); // Default Name

console.log(a.setPrivateName("A"));
console.log(b.setPrivateName("B"));
console.log(a.getPrivateName()); // A
console.log(b.getPrivateName()); // B

// private objects are not accessible outside the PrivateNamespaceClass function
console.log(a.p);

// the prototype functions are not re-created for each instance
// and instanceof still works
console.log(a.getPrivateName === b.getPrivateName);
console.log(a instanceof PrivateNamespaceClass);
console.log(b instanceof PrivateNamespaceClass);

J'adorerais avoir des commentaires de toute personne qui voit une erreur dans cette façon de faire.

Mims H. Wright
la source
4
Je suppose qu'une préoccupation potentielle est que toute instance pourrait accéder à d'autres instances privées vars en utilisant un identifiant d'instance différent. Pas nécessairement une mauvaise chose ...
Mims
15
Vous redéfinissez les fonctions du prototype à chaque appel du constructeur
Lu4
10
@ Lu4 Je ne suis pas sûr que ce soit vrai. Le constructeur est renvoyé à partir d'une fermeture; la seule fois où les fonctions prototypes sont définies est la première fois, dans cette expression de fonction immédiatement invoquée. Mis à part les problèmes de confidentialité mentionnés ci-dessus, cela me semble bon (à première vue).
guypursey
1
@ MimsH.Wright d'autres langages permettent d'accéder à d'autres objets privés de la même classe , mais uniquement lorsque vous y avez référence. Pour permettre cela, vous pouvez masquer les privés derrière une fonction qui prend le pointeur d'objets comme clé (comme associé à un ID). De cette façon, vous n'avez accès qu'aux données privées des objets que vous connaissez, ce qui est plus conforme à la portée dans d'autres langues. Cependant, cette implémentation met en lumière un problème plus profond avec cela. Les objets privés ne seront jamais récupérés avant que la fonction Constructor ne le soit.
Thomas Nadin
3
Je veux mentionner que cela ia été ajouté à toutes les instances. Ce n'est donc pas totalement «transparent» et ipourrait encore être falsifié.
Scott Rippey
18

voir la page de Doug Crockford à ce sujet . Vous devez le faire indirectement avec quelque chose qui peut accéder à la portée de la variable privée.

un autre exemple:

Incrementer = function(init) {
  var counter = init || 0;  // "counter" is a private variable
  this._increment = function() { return counter++; }
  this._set = function(x) { counter = x; }
}
Incrementer.prototype.increment = function() { return this._increment(); }
Incrementer.prototype.set = function(x) { return this._set(x); }

cas d'utilisation:

js>i = new Incrementer(100);
[object Object]
js>i.increment()
100
js>i.increment()
101
js>i.increment()
102
js>i.increment()
103
js>i.set(-44)
js>i.increment()
-44
js>i.increment()
-43
js>i.increment()
-42
Jason S
la source
47
Cet exemple semble être une pratique terrible. Le but d'utiliser des méthodes de prototype est que vous n'ayez pas à en créer une nouvelle pour chaque instance. Tu fais ça de toute façon. Pour chaque méthode, vous en créez une autre.
Kir
2
@ArmedMonkey Le concept semble valable, mais je suis d'accord que c'est un mauvais exemple car les fonctions prototypes présentées sont triviales. Si les fonctions prototypes étaient des fonctions beaucoup plus longues nécessitant un simple accès get / set aux variables «privées», cela aurait du sens.
crêpe
9
Pourquoi même s'embêter à exposer _setvia set? Pourquoi ne pas simplement le nommer setpour commencer?
Scott Rippey
15

Je suggère que ce serait probablement une bonne idée de décrire "avoir une affectation de prototype dans un constructeur" comme un anti-pattern Javascript. Pensez-y. C'est bien trop risqué.

Ce que vous faites là-bas lors de la création du deuxième objet (c.-à-d. B) redéfinit cette fonction prototype pour tous les objets qui utilisent ce prototype. Cela réinitialisera effectivement la valeur de l'objet a dans votre exemple. Cela fonctionnera si vous voulez une variable partagée et si vous créez toutes les instances d'objet à l'avance, mais cela semble trop risqué.

J'ai trouvé un bogue dans certains Javascript sur lesquels je travaillais récemment et qui était dû à cet anti-pattern exact. Il essayait de définir un gestionnaire de glisser-déposer sur l'objet particulier en cours de création, mais le faisait à la place pour toutes les instances. Pas bon.

La solution de Doug Crockford est la meilleure.

Lance Ewing
la source
10

@Kai

Cela ne fonctionnera pas. Si tu fais

var t2 = new TestClass();

alors t2.prototypeHelloaccédera à la section privée de t.

@AnglesCrimes

L'exemple de code fonctionne correctement, mais il crée en fait un membre privé «statique» partagé par toutes les instances. Ce n'est peut-être pas la solution recherchée par les morgancodes.

Jusqu'à présent, je n'ai pas trouvé de moyen simple et propre de le faire sans introduire un hachage privé et des fonctions de nettoyage supplémentaires. Une fonction de membre privé peut être simulée dans une certaine mesure:

(function() {
    function Foo() { ... }
    Foo.prototype.bar = function() {
       privateFoo.call(this, blah);
    };
    function privateFoo(blah) { 
        // scoped to the instance by passing this to call 
    }

    window.Foo = Foo;
}());
Tim
la source
Vous avez bien compris vos points, mais pouvez-vous expliquer ce que votre extrait de code essaie de faire?
Vishwanath
privateFooest complètement privé et donc invisible lors de l'obtention d'un fichier new Foo(). Seule bar()est une méthode publique ici, qui a accès à privateFoo. Vous pouvez utiliser le même mécanisme pour les variables simples et les objets, mais vous devez toujours garder à l'esprit que ceux-ci privatessont en fait statiques et seront partagés par tous les objets que vous créez.
Philzen
6

Oui c'est possible. Le modèle de conception PPF résout simplement cela.

PPF est l'acronyme de Private Prototype Functions. Le PPF de base résout ces problèmes:

  1. Les fonctions de prototype ont accès aux données d'instance privée.
  2. Les fonctions de prototype peuvent être rendues privées.

Pour le premier, juste:

  1. Placez toutes les variables d'instance privées que vous souhaitez rendre accessibles à partir des fonctions de prototype dans un conteneur de données distinct, et
  2. Passez une référence au conteneur de données à toutes les fonctions du prototype en tant que paramètre.

C'est si simple. Par exemple:

// Helper class to store private data.
function Data() {};

// Object constructor
function Point(x, y)
{
  // container for private vars: all private vars go here
  // we want x, y be changeable via methods only
  var data = new Data;
  data.x = x;
  data.y = y;

  ...
}

// Prototype functions now have access to private instance data
Point.prototype.getX = function(data)
{
  return data.x;
}

Point.prototype.getY = function(data)
{
  return data.y;
}

...

Lire l'histoire complète ici:

Modèle de conception PPF

Edward
la source
4
Les réponses aux liens uniquement sont généralement mal vues sur SO. Veuillez montrer un exemple.
Corey Adler
L'article a des exemples à l'intérieur, alors s'il vous plaît voir là
Edward
5
Que se passe-t-il si, plus tard, ce site tombe en panne? Comment quelqu'un est-il censé voir un exemple alors? La politique est en place afin que tout élément de valeur dans un lien puisse être conservé ici, et ne pas avoir à compter sur un site Web que cela n'est pas sous notre contrôle.
Corey Adler
3
@Edward, votre lien est une lecture intéressante! Cependant, il me semble que la raison principale pour accéder aux données privées à l'aide de fonctions prototypiques, est d'éviter que chaque objet gaspille de la mémoire avec des fonctions publiques identiques. La méthode que vous décrivez ne résout pas ce problème, car pour un usage public, une fonction prototypique doit être encapsulée dans une fonction publique ordinaire. Je suppose que le modèle pourrait être utile pour économiser de la mémoire si vous avez beaucoup de ppf qui sont combinés dans une seule fonction publique. Les utilisez-vous pour autre chose?
Dining Philosopher
@DiningPhilosofer, merci d'avoir apprécié mon article. Oui, vous avez raison, nous utilisons toujours des fonctions d'instance. Mais l'idée est de les avoir aussi légers que possible en rappelant simplement leurs homologues PPF qui font tout le travail difficile. Finalement, toutes les instances appellent les mêmes PPF (via des wrappers bien sûr), donc une certaine économie de mémoire peut être attendue. La question est combien. Je m'attends à des économies substantielles.
Edward le
5

Vous pouvez réellement y parvenir en utilisant la vérification des accesseurs :

(function(key, global) {
  // Creates a private data accessor function.
  function _(pData) {
    return function(aKey) {
      return aKey === key && pData;
    };
  }

  // Private data accessor verifier.  Verifies by making sure that the string
  // version of the function looks normal and that the toString function hasn't
  // been modified.  NOTE:  Verification can be duped if the rogue code replaces
  // Function.prototype.toString before this closure executes.
  function $(me) {
    if(me._ + '' == _asString && me._.toString === _toString) {
      return me._(key);
    }
  }
  var _asString = _({}) + '', _toString = _.toString;

  // Creates a Person class.
  var PersonPrototype = (global.Person = function(firstName, lastName) {
    this._ = _({
      firstName : firstName,
      lastName : lastName
    });
  }).prototype;
  PersonPrototype.getName = function() {
    var pData = $(this);
    return pData.firstName + ' ' + pData.lastName;
  };
  PersonPrototype.setFirstName = function(firstName) {
    var pData = $(this);
    pData.firstName = firstName;
    return this;
  };
  PersonPrototype.setLastName = function(lastName) {
    var pData = $(this);
    pData.lastName = lastName;
    return this;
  };
})({}, this);

var chris = new Person('Chris', 'West');
alert(chris.setFirstName('Christopher').setLastName('Webber').getName());

Cet exemple provient de mon article sur les fonctions prototypiques et les données privées et y est expliqué plus en détail.

Chris West
la source
1
Cette réponse est trop «intelligente» pour être utile, mais j'aime la réponse d'utiliser une variable liée IFFE comme une poignée de main secrète. Cette implémentation utilise trop de fermetures pour être utile; le but d'avoir des méthodes définies par prototype est d'empêcher la construction de nouveaux objets fonction pour chaque méthode sur chaque objet.
greg.kindel
Cette approche utilise une clé secrète pour identifier les méthodes de prototype qui sont fiables et celles qui ne le sont pas. Cependant, c'est l'instance qui valide la clé, donc la clé doit être envoyée à l'instance. Mais alors, un code non approuvé pourrait appeler une méthode de confiance sur une fausse instance, ce qui volerait la clé. Et avec cette clé, créez de nouvelles méthodes qui seraient considérées comme fiables par des instances réelles. Ce n'est donc que la confidentialité par l'obscurité.
Oriol
4

JavaScript actuel, je suis assez certain qu'il ya une et une seule façon d'avoir l' état privé , accessible à partir de prototypes fonctions, sans ajouter quoi que ce soit du public à this. La réponse est d'utiliser le modèle de «carte faible».

Pour résumer: la Personclasse a une seule carte faible, où les clés sont les instances de Person, et les valeurs sont des objets simples qui sont utilisés pour le stockage privé.

Voici un exemple entièrement fonctionnel: (jouez sur http://jsfiddle.net/ScottRippey/BLNVr/ )

var Person = (function() {
    var _ = weakMap();
    // Now, _(this) returns an object, used for private storage.
    var Person = function(first, last) {
        // Assign private storage:
        _(this).firstName = first;
        _(this).lastName = last;
    }
    Person.prototype = {
        fullName: function() {
            // Retrieve private storage:
            return _(this).firstName + _(this).lastName;
        },
        firstName: function() {
            return _(this).firstName;
        },
        destroy: function() {
            // Free up the private storage:
            _(this, true);
        }
    };
    return Person;
})();

function weakMap() {
    var instances=[], values=[];
    return function(instance, destroy) {
        var index = instances.indexOf(instance);
        if (destroy) {
            // Delete the private state:
            instances.splice(index, 1);
            return values.splice(index, 1)[0];
        } else if (index === -1) {
            // Create the private state:
            instances.push(instance);
            values.push({});
            return values[values.length - 1];
        } else {
            // Return the private state:
            return values[index];
        }
    };
}

Comme je l'ai dit, c'est vraiment le seul moyen de réaliser les 3 parties.

Il y a cependant deux mises en garde. Premièrement, cela coûte des performances - chaque fois que vous accédez aux données privées, c'est une O(n)opération, où nest le nombre d'instances. Vous ne voudrez donc pas faire cela si vous avez un grand nombre d'instances. Deuxièmement, lorsque vous avez terminé avec une instance, vous devez appeler destroy; sinon, l'instance et les données ne seront pas récupérées et vous vous retrouverez avec une fuite de mémoire.

Et c'est pourquoi ma réponse originale, "Vous ne devriez pas" , est quelque chose à quoi je voudrais m'en tenir.

Scott Rippey
la source
Si vous ne détruisez pas explicitement une instance de Person avant qu'elle ne soit hors de portée, la carte faible ne garde-t-elle pas une référence à celle-ci, vous aurez donc une fuite de mémoire? J'ai trouvé un modèle pour protégé car d'autres instances de Person peuvent accéder à la variable et celles héritant de Person peuvent. Je viens de le bricoler, donc je ne sais pas s'il y a des inconvénients autres qu'un traitement supplémentaire (cela ne ressemble pas à l'accès aux privés ) stackoverflow.com/a/21800194/1641941 Le retour d'un objet privé / protégé est une douleur depuis l'appel du code peut alors muter votre privé / protégé.
HMR
2
@HMR Oui, vous devez explicitement détruire les données privées. Je vais ajouter cette mise en garde à ma réponse.
Scott Rippey
3

Il existe un moyen plus simple en tirant parti de l'utilisation des méthodes bindet call.

En définissant des variables privées sur un objet, vous pouvez tirer parti de la portée de cet objet.

Exemple

function TestClass (value) {
    // The private value(s)
    var _private = {
        value: value
    };

    // `bind` creates a copy of `getValue` when the object is instantiated
    this.getValue = TestClass.prototype.getValue.bind(_private);

    // Use `call` in another function if the prototype method will possibly change
    this.getValueDynamic = function() {
        return TestClass.prototype.getValue.call(_private);
    };
};

TestClass.prototype.getValue = function() {
    return this.value;
};

Cette méthode n'est pas sans inconvénients. Étant donné que le contexte de portée est effectivement remplacé, vous n'avez pas d'accès en dehors de l' _privateobjet. Cependant, il n'est pas impossible de toujours donner accès à la portée de l'objet d'instance. Vous pouvez passer le contexte de l'objet ( this) comme deuxième argument à bindou callpour avoir toujours accès à ses valeurs publiques dans la fonction prototype.

Accéder aux valeurs publiques

function TestClass (value) {
    var _private = {
        value: value
    };

    this.message = "Hello, ";

    this.getMessage = TestClass.prototype.getMessage.bind(_private, this);

}

TestClass.prototype.getMessage = function(_public) {

    // Can still access passed in arguments
    // e.g. – test.getValues('foo'), 'foo' is the 2nd argument to the method
    console.log([].slice.call(arguments, 1));
    return _public.message + this.value;
};

var test = new TestClass("World");
test.getMessage(1, 2, 3); // [1, 2, 3]         (console.log)
                          // => "Hello, World" (return value)

test.message = "Greetings, ";
test.getMessage(); // []                    (console.log)
                   // => "Greetings, World" (return value)
thgaskell
la source
2
Pourquoi quelqu'un créerait-il une copie de la méthode prototype au lieu de simplement créer une méthode instanciée en premier lieu?
écraser
3

Essayez-le!

    function Potatoe(size) {
    var _image = new Image();
    _image.src = 'potatoe_'+size+'.png';
    function getImage() {
        if (getImage.caller == null || getImage.caller.owner != Potatoe.prototype)
            throw new Error('This is a private property.');
        return _image;
    }
    Object.defineProperty(this,'image',{
        configurable: false,
        enumerable: false,
        get : getImage          
    });
    Object.defineProperty(this,'size',{
        writable: false,
        configurable: false,
        enumerable: true,
        value : size            
    });
}
Potatoe.prototype.draw = function(ctx,x,y) {
    //ctx.drawImage(this.image,x,y);
    console.log(this.image);
}
Potatoe.prototype.draw.owner = Potatoe.prototype;

var pot = new Potatoe(32);
console.log('Potatoe size: '+pot.size);
try {
    console.log('Potatoe image: '+pot.image);
} catch(e) {
    console.log('Oops: '+e);
}
pot.draw();
AlanNLohse
la source
1
Cela repose sur caller, qui est une extension dépendante de l'implémentation non autorisée en mode strict.
Oriol
1

Voici ce que j'ai trouvé.

(function () {
    var staticVar = 0;
    var yrObj = function () {
        var private = {"a":1,"b":2};
        var MyObj = function () {
            private.a += staticVar;
            staticVar++;
        };
        MyObj.prototype = {
            "test" : function () {
                console.log(private.a);
            }
        };

        return new MyObj;
    };
    window.YrObj = yrObj;
}());

var obj1 = new YrObj;
var obj2 = new YrObj;
obj1.test(); // 1
obj2.test(); // 2

le principal problème de cette implémentation est qu'elle redéfinit les prototypes à chaque instanciation.

Xeltor
la source
Intéressant, j'aime vraiment la tentative et je pensais à la même chose, mais vous avez raison de dire que redéfinir la fonction prototype à chaque instanciation est une limitation assez importante. Ce n'est pas seulement parce que c'est gaspillé des cycles de processeur, mais parce que si jamais vous changez le prototoype plus tard, il serait "réinitialisé" à son état d'origine tel que défini dans le constructeur lors de la prochaine instanciation: /
Niko Bellic
1
Cela a non seulement redéfini les prototypes, mais définit un nouveau constructeur pour chaque instance. Les "instances" ne sont donc plus des instances de la même classe.
Oriol
1

Il existe un moyen très simple de le faire

function SharedPrivate(){
  var private = "secret";
  this.constructor.prototype.getP = function(){return private}
  this.constructor.prototype.setP = function(v){ private = v;}
}

var o1 = new SharedPrivate();
var o2 = new SharedPrivate();

console.log(o1.getP()); // secret
console.log(o2.getP()); // secret
o1.setP("Pentax Full Frame K1 is on sale..!");
console.log(o1.getP()); // Pentax Full Frame K1 is on sale..!
console.log(o2.getP()); // Pentax Full Frame K1 is on sale..!
o2.setP("And it's only for $1,795._");
console.log(o1.getP()); // And it's only for $1,795._

Les prototypes JavaScript sont en or.

Redu
la source
2
Je pense qu'il vaut mieux ne pas utiliser de prototype dans la fonction constructeur car cela créera une nouvelle fonction à chaque fois qu'une nouvelle instance est créée.
whamsicore
@whamsicore Oui, c'est vrai, mais dans ce cas, c'est essentiel car pour chaque objet instancié, nous devons organiser une fermeture partagée. C'est la raison pour laquelle les définitions de fonction résident à l'intérieur du constructeur et nous devons nous référer au SharedPrivate.prototypecar this.constructor.prototypece n'est pas grave de redéfinir getP et setP plusieurs fois ...
Redu
1

Je suis en retard à la fête, mais je pense que je peux contribuer. Ici, vérifiez 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) auquel on ne peut accéder 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 que c'est vraiment puissant.

guitarino
la source
0

Vous ne pouvez pas placer les variables dans une portée plus élevée?

(function () {
    var privateVariable = true;

    var MyClass = function () {
        if (privateVariable) console.log('readable from private scope!');
    };

    MyClass.prototype.publicMethod = function () {
        if (privateVariable) console.log('readable from public scope!');
    };
}))();
Ev Haus
la source
4
Ensuite, les variables sont partagées entre toutes les instances de MyClass.
écraser
0

Vous pouvez également essayer d'ajouter une méthode non pas directement sur le prototype, mais sur une fonction constructeur comme celle-ci:

var MyArray = function() {
    var array = [];

    this.add = MyArray.add.bind(null, array);
    this.getAll = MyArray.getAll.bind(null, array);
}

MyArray.add = function(array, item) {
    array.push(item);
}
MyArray.getAll = function(array) {
    return array;
}

var myArray1 = new MyArray();
myArray1.add("some item 1");
console.log(myArray1.getAll()); // ['some item 1']
var myArray2 = new MyArray();
myArray2.add("some item 2");
console.log(myArray2.getAll()); // ['some item 2']
console.log(myArray1.getAll()); // ['some item 2'] - FINE!
Maciej Dzikowicki
la source
0

Voici quelque chose que j'ai trouvé en essayant de trouver la solution la plus simple à ce problème, peut-être que cela pourrait être utile à quelqu'un. Je suis nouveau sur javascript, donc il pourrait bien y avoir des problèmes avec le code.

// pseudo-class definition scope
(function () {

    // this is used to identify 'friend' functions defined within this scope,
    // while not being able to forge valid parameter for GetContext() 
    // to gain 'private' access from outside
    var _scope = new (function () { })();
    // -----------------------------------------------------------------

    // pseudo-class definition
    this.Something = function (x) {

        // 'private' members are wrapped into context object,
        // it can be also created with a function
        var _ctx = Object.seal({

            // actual private members
            Name: null,
            Number: null,

            Somefunc: function () {
                console.log('Something(' + this.Name + ').Somefunc(): number = ' + this.Number);
            }
        });
        // -----------------------------------------------------------------

        // function below needs to be defined in every class
        // to allow limited access from prototype
        this.GetContext = function (scope) {

            if (scope !== _scope) throw 'access';
            return _ctx;
        }
        // -----------------------------------------------------------------

        {
            // initialization code, if any
            _ctx.Name = (x !== 'undefined') ? x : 'default';
            _ctx.Number = 0;

            Object.freeze(this);
        }
    }
    // -----------------------------------------------------------------

    // prototype is defined only once
    this.Something.prototype = Object.freeze({

        // public accessors for 'private' field
        get Number() { return this.GetContext(_scope).Number; },
        set Number(v) { this.GetContext(_scope).Number = v; },

        // public function making use of some private fields
        Test: function () {

            var _ctx = this.GetContext(_scope);
            // access 'private' field
            console.log('Something(' + _ctx.Name + ').Test(): ' + _ctx.Number);
            // call 'private' func
            _ctx.Somefunc();
        }
    });
    // -----------------------------------------------------------------

    // wrap is used to hide _scope value and group definitions
}).call(this);

function _A(cond) { if (cond !== true) throw new Error('assert failed'); }
// -----------------------------------------------------------------

function test_smth() {

    console.clear();

    var smth1 = new Something('first'),
      smth2 = new Something('second');

    //_A(false);
    _A(smth1.Test === smth2.Test);

    smth1.Number = 3;
    smth2.Number = 5;
    console.log('smth1.Number: ' + smth1.Number + ', smth2.Number: ' + smth2.Number);

    smth1.Number = 2;
    smth2.Number = 6;

    smth1.Test();
    smth2.Test();

    try {
        var ctx = smth1.GetContext();
    } catch (err) {
        console.log('error: ' + err);
    }
}

test_smth();
V.Mihaly4
la source
0

J'ai fait face exactement à la même question aujourd'hui et après avoir développé la réponse de première classe de Scott Rippey, j'ai proposé une solution très simple (IMHO) qui est à la fois compatible avec ES5 et efficace, c'est aussi nom clash safe (utiliser _private semble dangereux) .

/*jslint white: true, plusplus: true */

 /*global console */

var a, TestClass = (function(){
    "use strict";
    function PrefixedCounter (prefix) {
        var counter = 0;
        this.count = function () {
            return prefix + (++counter);
        };
    }
    var TestClass = (function(){
        var cls, pc = new PrefixedCounter("_TestClass_priv_")
        , privateField = pc.count()
        ;
        cls = function(){
            this[privateField] = "hello";
            this.nonProtoHello = function(){
                console.log(this[privateField]);
            };
        };
        cls.prototype.prototypeHello = function(){
            console.log(this[privateField]);
        };
        return cls;
    }());
    return TestClass;
}());

a = new TestClass();
a.nonProtoHello();
a.prototypeHello();

Testé avec ringojs et nodejs. J'ai hâte de lire votre opinion.

Alexgirao
la source
Voici une référence: consultez la section «Un pas de plus». philipwalton.com/articles/…
jimasun
0
var getParams = function(_func) {
  res = _func.toString().split('function (')[1].split(')')[0].split(',')
  return res
}

function TestClass(){

  var private = {hidden: 'secret'}
  //clever magic accessor thing goes here
  if ( !(this instanceof arguments.callee) ) {
    for (var key in arguments) {
      if (typeof arguments[key] == 'function') {
        var keys = getParams(arguments[key])
        var params = []
        for (var i = 0; i <= keys.length; i++) {
          if (private[keys[i]] != undefined) {
            params.push(private[keys[i]])
          }
        }
        arguments[key].apply(null,params)
      }
    }
  }
}


TestClass.prototype.test = function(){
  var _hidden; //variable I want to get
  TestClass(function(hidden) {_hidden = hidden}) //invoke magic to get
};

new TestClass().test()

Comment c'est? Utilisation d'un accesseur privé. Vous permet uniquement d'obtenir les variables mais de ne pas les définir, cela dépend du cas d'utilisation.

dylan0150
la source
Cela ne répond pas à la question de manière utile. pourquoi pensez-vous que c'est la réponse? comment ça marche? Le simple fait de dire à quelqu'un de changer son code sans aucun contexte ni aucune signification ne les aide pas à apprendre ce qu'ils ont mal fait.
GrumpyCrouton
Il voulait un moyen d'accéder aux variables privées cachées d'une classe via des prototypes sans avoir à créer cette variable cachée sur chaque instance de la classe. Le code ci-dessus est un exemple de méthode pour faire cela. En quoi n'est-ce pas une réponse à la question?
dylan0150
Je n'ai pas dit que ce n'était pas une réponse à la question. J'ai dit que ce n'était pas une réponse utile , car cela n'aide personne à apprendre. Vous devez expliquer votre code, pourquoi il fonctionne, pourquoi c'est la bonne façon de le faire. Si j'étais l'auteur de la question, je n'accepterais pas votre réponse car elle n'encourage pas l'apprentissage, elle ne m'apprend pas ce que je fais de mal ou ce que fait le code donné ou comment il fonctionne.
GrumpyCrouton
0

J'ai une solution, mais je ne suis pas sûr qu'elle soit sans défauts.

Pour que cela fonctionne, vous devez utiliser la structure suivante:

  1. Utilisez 1 objet privé contenant toutes les variables privées.
  2. Utilisez 1 fonction d'instance.
  3. Appliquez une fermeture au constructeur et à toutes les fonctions du prototype.
  4. Toute instance créée est effectuée en dehors de la fermeture définie.

Voici le code:

var TestClass = 
(function () {
    // difficult to be guessed.
    var hash = Math.round(Math.random() * Math.pow(10, 13) + + new Date());
    var TestClass = function () {
        var privateFields = {
            field1: 1,
            field2: 2
        };
        this.getPrivateFields = function (hashed) {
            if(hashed !== hash) {
                throw "Cannot access private fields outside of object.";
                // or return null;
            }
            return privateFields;
        };
    };

    TestClass.prototype.prototypeHello = function () {
        var privateFields = this.getPrivateFields(hash);
        privateFields.field1 = Math.round(Math.random() * 100);
        privateFields.field2 = Math.round(Math.random() * 100);
    };

    TestClass.prototype.logField1 = function () {
        var privateFields = this.getPrivateFields(hash);
        console.log(privateFields.field1);
    };

    TestClass.prototype.logField2 = function () {
        var privateFields = this.getPrivateFields(hash);
        console.log(privateFields.field2);
    };

    return TestClass;
})();

La façon dont cela fonctionne est qu'elle fournit une fonction d'instance "this.getPrivateFields" pour accéder à l'objet de variables privées "privateFields", mais cette fonction ne retournera que l'objet "privateFields" à l'intérieur de la fermeture principale définie (également des fonctions prototypes utilisant "this.getPrivateFields "doivent être définis à l'intérieur de cette fermeture).

Un hachage produit pendant l'exécution et difficile à deviner est utilisé comme paramètre pour s'assurer que même si "getPrivateFields" est appelé en dehors de la portée de la fermeture ne retournera pas l'objet "privateFields".

L'inconvénient est que nous ne pouvons pas étendre TestClass avec plus de fonctions prototypes en dehors de la fermeture.

Voici un code de test:

var t1 = new TestClass();
console.log('Initial t1 field1 is: ');
t1.logField1();
console.log('Initial t1 field2 is: ');
t1.logField2();
t1.prototypeHello();
console.log('t1 field1 is now: ');
t1.logField1();
console.log('t1 field2 is now: ');
t1.logField2();
var t2 = new TestClass();
console.log('Initial t2 field1 is: ');
t2.logField1();
console.log('Initial t2 field2 is: ');
t2.logField2();
t2.prototypeHello();
console.log('t2 field1 is now: ');
t2.logField1();
console.log('t2 field2 is now: ');
t2.logField2();

console.log('t1 field1 stays: ');
t1.logField1();
console.log('t1 field2 stays: ');
t1.logField2();

t1.getPrivateFields(11233);

EDIT: En utilisant cette méthode, il est également possible de "définir" des fonctions privées.

TestClass.prototype.privateFunction = function (hashed) {
    if(hashed !== hash) {
        throw "Cannot access private function.";
    }
};

TestClass.prototype.prototypeHello = function () {
    this.privateFunction(hash);
};
Jannes Botis
la source
0

Je jouais avec ça aujourd'hui et c'était la seule solution que je pouvais trouver sans utiliser de symboles. La meilleure chose à ce sujet est que tout peut être complètement privé.

La solution est basée sur un chargeur de module local qui devient essentiellement le médiateur d'un cache de stockage privé (en utilisant une carte faible).

   const loader = (function() {
        function ModuleLoader() {}

    //Static, accessible only if truly needed through obj.constructor.modules
    //Can also be made completely private by removing the ModuleLoader prefix.
    ModuleLoader.modulesLoaded = 0;
    ModuleLoader.modules = {}

    ModuleLoader.prototype.define = function(moduleName, dModule) {
        if (moduleName in ModuleLoader.modules) throw new Error('Error, duplicate module');

        const module = ModuleLoader.modules[moduleName] = {}

        module.context = {
            __moduleName: moduleName,
            exports: {}
        }

        //Weak map with instance as the key, when the created instance is garbage collected or goes out of scope this will be cleaned up.
        module._private = {
            private_sections: new WeakMap(),
            instances: []
        };

        function private(action, instance) {
            switch (action) {
                case "create":
                    if (module._private.private_sections.has(instance)) throw new Error('Cannot create private store twice on the same instance! check calls to create.')
                    module._private.instances.push(instance);
                    module._private.private_sections.set(instance, {});
                    break;
                case "delete":
                    const index = module._private.instances.indexOf(instance);
                    if (index == -1) throw new Error('Invalid state');
                    module._private.instances.slice(index, 1);
                    return module._private.private_sections.delete(instance);
                    break;
                case "get":
                    return module._private.private_sections.get(instance);
                    break;
                default:
                    throw new Error('Invalid action');
                    break;
            }
        }

        dModule.call(module.context, private);
        ModuleLoader.modulesLoaded++;
    }

    ModuleLoader.prototype.remove = function(moduleName) {
        if (!moduleName in (ModuleLoader.modules)) return;

        /*
            Clean up as best we can.
        */
        const module = ModuleLoader.modules[moduleName];
        module.context.__moduleName = null;
        module.context.exports = null;
        module.cotext = null;
        module._private.instances.forEach(function(instance) { module._private.private_sections.delete(instance) });
        for (let i = 0; i < module._private.instances.length; i++) {
            module._private.instances[i] = undefined;
        }
        module._private.instances = undefined;
        module._private = null;
        delete ModuleLoader.modules[moduleName];
        ModuleLoader.modulesLoaded -= 1;
    }


    ModuleLoader.prototype.require = function(moduleName) {
        if (!(moduleName in ModuleLoader.modules)) throw new Error('Module does not exist');

        return ModuleLoader.modules[moduleName].context.exports;
    }



     return new ModuleLoader();
    })();

    loader.define('MyModule', function(private_store) {
        function MyClass() {
            //Creates the private storage facility. Called once in constructor.
            private_store("create", this);


            //Retrieve the private storage object from the storage facility.
            private_store("get", this).no = 1;
        }

        MyClass.prototype.incrementPrivateVar = function() {
            private_store("get", this).no += 1;
        }

        MyClass.prototype.getPrivateVar = function() {
            return private_store("get", this).no;
        }

        this.exports = MyClass;
    })

    //Get whatever is exported from MyModule
    const MyClass = loader.require('MyModule');

    //Create a new instance of `MyClass`
    const myClass = new MyClass();

    //Create another instance of `MyClass`
    const myClass2 = new MyClass();

    //print out current private vars
    console.log('pVar = ' + myClass.getPrivateVar())
    console.log('pVar2 = ' + myClass2.getPrivateVar())

    //Increment it
    myClass.incrementPrivateVar()

    //Print out to see if one affected the other or shared
    console.log('pVar after increment = ' + myClass.getPrivateVar())
    console.log('pVar after increment on other class = ' + myClass2.getPrivateVar())

    //Clean up.
    loader.remove('MyModule')
Eladian
la source
0

Je sais que cela fait plus d'une décennie que cela a été demandé, mais je viens de réfléchir à cela pour la n-ième fois de ma vie de programmeur et j'ai trouvé une solution possible que je ne sais pas si j'aime encore entièrement . Je n'ai pas vu cette méthodologie documentée auparavant, donc je la nommerai le "modèle dollar privé / public" ou _ $ / $ modèle .

var ownFunctionResult = this.$("functionName"[, arg1[, arg2 ...]]);
var ownFieldValue = this._$("fieldName"[, newValue]);

var objectFunctionResult = objectX.$("functionName"[, arg1[, arg2 ...]]);

//Throws an exception. objectX._$ is not defined
var objectFieldValue = objectX._$("fieldName"[, newValue]);

Le concept utilise une fonction ClassDefinition qui renvoie une fonction Constructor qui renvoie un objet Interface . La seule méthode de l'interface est celle $qui reçoit un nameargument pour appeler la fonction correspondante dans l'objet constructeur, tous les arguments supplémentaires passés après namesont passés lors de l'invocation.

La fonction d'assistance définie globalement ClassValuesstocke tous les champs d' un objet selon les besoins. Il définit la _$fonction pour y accéder par name. Ceci suit un court modèle get / set donc s'il valueest passé, il sera utilisé comme nouvelle valeur de variable.

var ClassValues = function (values) {
  return {
    _$: function _$(name, value) {
      if (arguments.length > 1) {
        values[name] = value;
      }

      return values[name];
    }
  };
};

La fonction définie globalement Interfaceprend un objet et un Valuesobjet pour renvoyer un _interfaceavec une seule fonction $qui examine objpour trouver une fonction nommée d'après le paramètre nameet l'invoque avec valuescomme objet de portée . Les arguments supplémentaires passés à $seront transmis lors de l'appel de la fonction.

var Interface = function (obj, values, className) {
  var _interface = {
    $: function $(name) {
      if (typeof(obj[name]) === "function") {
        return obj[name].apply(values, Array.prototype.splice.call(arguments, 1));
      }

      throw className + "." + name + " is not a function.";
    }
  };

  //Give values access to the interface.
  values.$ = _interface.$;

  return _interface;
};

Dans l'exemple ci-dessous, ClassXest affecté au résultat de ClassDefinition, qui est la Constructorfonction. Constructorpeut recevoir n'importe quel nombre d'arguments. Interfaceest ce que le code externe obtient après avoir appelé le constructeur.

var ClassX = (function ClassDefinition () {
  var Constructor = function Constructor (valA) {
    return Interface(this, ClassValues({ valA: valA }), "ClassX");
  };

  Constructor.prototype.getValA = function getValA() {
    //private value access pattern to get current value.
    return this._$("valA");
  };

  Constructor.prototype.setValA = function setValA(valA) {
    //private value access pattern to set new value.
    this._$("valA", valA);
  };

  Constructor.prototype.isValAValid = function isValAValid(validMessage, invalidMessage) {
    //interface access pattern to call object function.
    var valA = this.$("getValA");

    //timesAccessed was not defined in constructor but can be added later...
    var timesAccessed = this._$("timesAccessed");

    if (timesAccessed) {
      timesAccessed = timesAccessed + 1;
    } else {
      timesAccessed = 1;
    }

    this._$("timesAccessed", timesAccessed);

    if (valA) {
      return "valA is " + validMessage + ".";
    }

    return "valA is " + invalidMessage + ".";
  };

  return Constructor;
}());

Il est inutile d'avoir des fonctions non prototypées dans Constructor, bien que vous puissiez les définir dans le corps de la fonction constructeur. Toutes les fonctions sont appelées avec le modèle de dollar public this.$("functionName"[, param1[, param2 ...]]) . Les valeurs privées sont accessibles avec le modèle de dollar privé this._$("valueName"[, replacingValue]); . Comme Interfacen'a pas de définition pour _$, les valeurs ne sont pas accessibles par les objets externes. Puisque chaque corps de fonction prototypé thisest défini sur l' valuesobjet dans function $, vous obtiendrez des exceptions si vous appelez directement les fonctions frères Constructor; le modèle _ $ / $ doit également être suivi dans les corps de fonctions prototypés. Ci-dessous un exemple d'utilisation.

var classX1 = new ClassX();
console.log("classX1." + classX1.$("isValAValid", "valid", "invalid"));
console.log("classX1.valA: " + classX1.$("getValA"));
classX1.$("setValA", "v1");
console.log("classX1." + classX1.$("isValAValid", "valid", "invalid"));
var classX2 = new ClassX("v2");
console.log("classX1.valA: " + classX1.$("getValA"));
console.log("classX2.valA: " + classX2.$("getValA"));
//This will throw an exception
//classX1._$("valA");

Et la sortie de la console.

classX1.valA is invalid.
classX1.valA: undefined
classX1.valA is valid.
classX1.valA: v1
classX2.valA: v2

Le modèle _ $ / $ permet une confidentialité totale des valeurs dans des classes entièrement prototypées. Je ne sais pas si j'utiliserai jamais ça, ni s'il a des défauts, mais bon, c'était un bon puzzle!

JPortillo
la source
0

Cartes faibles ES6

En utilisant un modèle simple basé sur ES6, WeakMaps est possible d'obtenir des variables membres privées, accessibles à partir des fonctions prototypes .

Remarque: l'utilisation de WeakMaps garantit la sécurité contre les fuites de mémoire , en permettant au Garbage Collector d'identifier et d'éliminer les instances inutilisées.

// Create a private scope using an Immediately 
// Invoked Function Expression...
let Person = (function() {

    // Create the WeakMap that will hold each  
    // Instance collection's of private data
    let privateData = new WeakMap();
    
    // Declare the Constructor :
    function Person(name) {
        // Insert the private data in the WeakMap,
        // using 'this' as a unique acces Key
        privateData.set(this, { name: name });
    }
    
    // Declare a prototype method 
    Person.prototype.getName = function() {
        // Because 'privateData' is in the same 
        // scope, it's contents can be retrieved...
        // by using  again 'this' , as  the acces key 
        return privateData.get(this).name;
    };

    // return the Constructor
    return Person;
}());

Une explication plus détaillée de ce modèle peut être trouvée ici

colxi
la source
-1

Vous devez changer 3 choses dans votre code:

  1. Remplacez var privateField = "hello"par this.privateField = "hello".
  2. Dans le prototype, remplacer privateFieldpar this.privateField.
  3. Dans le non-prototype, remplacez également privateFieldpar this.privateField.

Le code final serait le suivant:

TestClass = function(){
    this.privateField = "hello";
    this.nonProtoHello = function(){alert(this.privateField)};
}

TestClass.prototype.prototypeHello = function(){alert(this.privateField)};

var t = new TestClass();

t.prototypeHello()
A-Sharabiani
la source
this.privateFieldne serait pas un champ privé. il est accessible de l'extérieur:t.privateField
V. Rubinetti
-2

Vous pouvez utiliser une affectation de prototype dans la définition du constructeur.

La variable sera visible par la méthode prototype ajoutée mais toutes les instances des fonctions accèderont à la même variable SHARED.

function A()
{
  var sharedVar = 0;
  this.local = "";

  A.prototype.increment = function(lval)
  {    
    if (lval) this.local = lval;    
    alert((++sharedVar) + " while this.p is still " + this.local);
  }
}

var a = new A();
var b = new A();    
a.increment("I belong to a");
b.increment("I belong to b");
a.increment();
b.increment();

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

AngesCrimes
la source