D'après ma compréhension de javascript, les méthodes de prototype ne peuvent pas accéder à des variables qui sont privées à la portée du constructeur,
var Foo = function() {
var myprivate = 'I am private';
this.mypublic = 'I am public';
}
Foo.prototype = {
alertPublic: function() { alert(this.mypublic); } // will work
alertPrivate: function() { alert(myprivate); } // won't work
}
Cela est parfaitement logique, mais y a-t-il un moyen de contourner cela qui soit une bonne pratique sûre? Étant donné que l'utilisation de prototypes offre un avantage en termes de performances dans la mesure où les fonctions membres ne sont allouées qu'une seule fois, j'aimerais obtenir une fonctionnalité similaire tout en étant en mesure d'accéder à mes variables privées. Je ne pense pas que cela fonctionnera en utilisant un prototype, mais y a-t-il un autre modèle, comme une méthode d'usine ou une approche de fermeture? Quelque chose comme,
var fooFactory = function() {
var _alertPrivate = function(p) { alert(p); } // bulk of the logic goes here
return function(args) {
var foo = {};
var myprivate = args.someVar;
foo.mypublic = args.someOtherVar;
foo.alertPrivate = function() { _alertPrivate(myprivate); };
return foo;
};
}
var makeFoo = new fooFactory();
var foo = makeFoo(args);
Je ne sais pas si une nouvelle copie de _alertPrivate est créée chaque fois que je crée un nouveau Foo ou s'il y a un avantage potentiel en termes de performances. L'intention est d'obtenir une fonctionnalité similaire au prototypage (dans la mesure où elle économise de la mémoire) tout en pouvant accéder à des variables privées.
Merci.
3 réponses
J'ai trouvé le modèle suivant pour résoudre ce problème, du moins pour l'instant. Ce dont j'avais besoin était un setter privilégié afin qu'une variable privée puisse être modifiée depuis l'intérieur de certaines fonctions prototypes mais pas depuis n'importe où ailleurs:
var Foo = (function() {
// the bulk of the objects behavior goes here and is created once
var functions = {
update: function(a) {
a['privateVar'] = "Private variable set from the prototype";
}
};
// the objects prototype, also created once
var proto = {
Update: function() {
this.caller('update');
}
};
// special function to get private vars into scope
var hoist = function(accessor) {
return function(key) {
return functions[key](accessor());
}
}
// the constructor itself
var foo = function foo() {
var state = {
privateVar: "Private variable set in constructor",
// put more private vars here
}
this.caller = hoist(function(){
return state;
});
}
// assign the prototype
foo.prototype = proto;
// return the constructor
return foo;
})();
Fondamentalement, un pointeur vers l'état interne des objets est hissé vers son prototype via une fermeture sur une fonction d'accesseur simple () {état de retour; }. L'utilisation de la fonction «appelant» sur une instance donnée vous permet d'appeler des fonctions qui ne sont créées qu'une seule fois mais peuvent toujours faire référence à l'état privé détenu dans cette instance. Il est également important de noter qu'aucune fonction en dehors du prototype ne pourra jamais accéder à l'accesseur privilégié, car «l'appelant» n'accepte qu'une clé renvoyant aux fonctions prédéfinies qui sont dans le champ d'application.
Voici quelques repères de cette méthode pour voir comment elle se compare au prototypage pur. Ces chiffres représentent la création de 80 000 instances de l'objet dans une boucle (notez que l'objet utilisé pour l'analyse comparative est plus complexe que celui ci-dessus, qui était juste à des fins de simplification):
CHROME:
Fermeture uniquement - 2172 ms
Prototypage (ci-dessus) - 822 ms
Prototypage (voie standard) - 751 ms
FIREFOX:
Fermeture uniquement - 1528 ms
Prototypage (au dessus) - 971ms
Prototypage (voie standard) - 752 ms
Comme vous pouvez le voir, la méthode est presque aussi rapide que le prototypage normal, et certainement plus rapide que l'utilisation d'une fermeture normale qui copie les fonctions avec l'instance.
Ce que vous demandez est possible, mais il y aura toujours un compromis entre les performances (en vitesse ou en mémoire) et la fonctionnalité.
En JavaScript, il est possible d'obtenir un état privé par instance, avec des méthodes de prototypage normales (et sans stockage centralisé, qui fuit, sur le terrain).
Consultez l'article que j'ai écrit sur la technique: http://www.codeproject.com/KB/ ajax / SafeFactoryPattern.aspx
Ou accédez directement au code source dans: https://github.com/dcleao/private-state.
J'ai trouvé la réponse de Sean Thoman très utile (bien que difficile à comprendre au début).
Il ne semblait pas que le setter public puisse accepter une valeur pour privateVar
donc j'ai fait quelques ajustements:
Modifiez update
en functions
:
update: function(st, newVal) {
st['privateVar'] = newVal;
}
Modifiez Update
dans le proto
:
Update: function(newVal) {
this.caller('update', newVal);
}
Modifiez hoist
:
var hoist = function(accessor) {
return function(key) {
// we can't slice arguments directly because it's not a real array
var args_tail = Array.prototype.slice.call(arguments, 1);
return functions[key].apply(functions[key], [accessor()].concat(args_tail));
}
}
Questions connexes
De nouvelles questions
javascript
Pour des questions concernant la programmation dans ECMAScript (JavaScript / JS) et ses divers dialectes / implémentations (hors ActionScript). Veuillez inclure toutes les balises pertinentes dans votre question; par exemple, [node.js], [jquery], [json], etc.