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 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.";
}
};
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() {
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 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"));
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!