Ich weiß, dass es mehr als ein Jahrzehnt her ist, seit dies gefragt wurde, aber ich habe gerade zum n-ten Mal in meinem Programmiererleben darüber nachgedacht und eine mögliche Lösung gefunden, die ich noch nicht ganz mag . Ich habe diese Methode noch nicht dokumentiert gesehen, daher werde ich sie als "privates / öffentliches Dollar-Muster" oder _ $ / $ -Muster bezeichnen .
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]);
Das Konzept verwendet eine ClassDefinition- Funktion, die eine Konstruktorfunktion zurückgibt, die ein Interface- Objekt zurückgibt . Die einzige Methode $
der Schnittstelle besteht darin , ein name
Argument zu empfangen , um die entsprechende Funktion im Konstruktorobjekt aufzurufen, und alle zusätzlichen Argumente, die danach übergeben werdenname
werden, werden im Aufruf übergeben.
Die global definierte Hilfsfunktion ClassValues
speichert alle Felder in einem Objekt nach Bedarf. Es definiert die _$
Funktion, über die auf sie zugegriffen werden soll name
. Dies folgt einem kurzen get / set-Muster. Wenn value
es übergeben wird, wird es als neuer Variablenwert verwendet.
var ClassValues = function (values) {
return {
_$: function _$(name, value) {
if (arguments.length > 1) {
values[name] = value;
}
return values[name];
}
};
};
Die global definierte Funktion verwendet Interface
ein Objekt und ein Values
Objekt, um eine _interface
Funktion mit einer einzigen Funktion zurückzugeben $
, die untersucht obj
, ob eine nach dem Parameter benannte Funktion gefunden wurde , name
und sie values
als Objekt mit Gültigkeitsbereich aufruft . Die an übergebenen zusätzlichen Argumente $
werden beim Funktionsaufruf übergeben.
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;
};
Im folgenden Beispiel ClassX
wird das Ergebnis von zugeordnet ClassDefinition
, welches die Constructor
Funktion ist. Constructor
kann eine beliebige Anzahl von Argumenten erhalten. Interface
ist, was externer Code nach dem Aufrufen des Konstruktors erhält.
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;
}());
Es macht keinen Sinn, nicht prototypisierte Funktionen zu haben Constructor
, obwohl Sie sie im Konstruktorfunktionskörper definieren könnten. Alle Funktionen werden mit dem öffentlichen Dollar-Muster aufgerufen this.$("functionName"[, param1[, param2 ...]])
. Auf die privaten Werte wird mit dem privaten Dollarmuster zugegriffen this._$("valueName"[, replacingValue]);
. Da Interface
es keine Definition für gibt _$
, können externe Objekte nicht auf die Werte zugreifen. Da jeder Prototyp eines Funktionskörpers this
auf das values
Objekt in der Funktion festgelegt ist $
, erhalten Sie Ausnahmen, wenn Sie Constructor-Geschwisterfunktionen direkt aufrufen. Das _ $ / $ -Muster muss auch in prototypisierten Funktionskörpern befolgt werden. Unten Beispielverwendung.
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");
Und die Konsolenausgabe.
classX1.valA is invalid.
classX1.valA: undefined
classX1.valA is valid.
classX1.valA: v1
classX2.valA: v2
Das _ $ / $ -Muster ermöglicht die vollständige Vertraulichkeit von Werten in Klassen mit vollständigem Prototyp. Ich weiß nicht, ob ich das jemals benutzen werde oder ob es Fehler hat, aber hey, es war ein gutes Rätsel!