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 ...]]);
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 name
argument pour appeler la fonction correspondante dans l'objet constructeur, tous les arguments supplémentaires passés après name
sont passés lors de l'invocation.
La fonction d'assistance définie globalement ClassValues
stocke 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 value
est 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 Interface
prend un objet et un Values
objet pour renvoyer un _interface
avec une seule fonction $
qui examine obj
pour trouver une fonction nommée d'après le paramètre name
et l'invoque avec values
comme 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.";
}
};
values.$ = _interface.$;
return _interface;
};
Dans l'exemple ci-dessous, ClassX
est affecté au résultat de ClassDefinition
, qui est la Constructor
fonction. Constructor
peut recevoir n'importe quel nombre d'arguments. Interface
est 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() {
return this._$("valA");
};
Constructor.prototype.setValA = function setValA(valA) {
this._$("valA", valA);
};
Constructor.prototype.isValAValid = function isValAValid(validMessage, invalidMessage) {
var valA = this.$("getValA");
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 Interface
n'a pas de définition pour _$
, les valeurs ne sont pas accessibles par les objets externes. Puisque chaque corps de fonction prototypé this
est défini sur l' values
objet 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"));
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!