So che è passato più di 1 decennio da quando è stato chiesto, ma ho appena riflettuto su questo per l'ennesima volta nella mia vita da programmatore, e ho trovato una possibile soluzione che non so se mi piace ancora del tutto . Non ho mai visto questa metodologia documentata prima, quindi la chiamerò "modello dollaro privato / pubblico" o modello $ / $ .
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]);
Il concetto utilizza una funzione ClassDefinition che restituisce una funzione di costruzione che restituisce un oggetto Interface . L'unico metodo dell'interfaccia è quello di $
ricevere un name
argomento per invocare la funzione corrispondente nell'oggetto costruttore, eventuali argomenti aggiuntivi passati doponame
vengono passati .
La funzione helper definita a livello globale ClassValues
memorizza tutti i campi in un oggetto secondo necessità. Definisce la _$
funzione per accedervi name
. Questo segue un breve modello get / set, quindi se value
viene passato, verrà utilizzato come nuovo valore della variabile.
var ClassValues = function (values) {
return {
_$: function _$(name, value) {
if (arguments.length > 1) {
values[name] = value;
}
return values[name];
}
};
};
La funzione definita globalmente Interface
accetta un oggetto e un Values
oggetto per restituire un oggetto _interface
con una singola funzione $
che esamina obj
per trovare una funzione denominata dopo il parametro name
e lo richiama values
come oggetto con ambito . Gli argomenti aggiuntivi passati $
verranno passati all'invocazione della funzione.
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;
};
Nell'esempio seguente, ClassX
è assegnato al risultato di ClassDefinition
, che è la Constructor
funzione. Constructor
può ricevere un numero qualsiasi di argomenti. Interface
è ciò che ottiene il codice esterno dopo aver chiamato il costruttore.
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;
}());
Non ha senso avere funzioni non prototipate Constructor
, sebbene sia possibile definirle nel corpo della funzione di costruzione. Tutte le funzioni sono chiamate con il modello di dollaro pubblico this.$("functionName"[, param1[, param2 ...]])
. È possibile accedere ai valori privati con il modello del dollaro privato this._$("valueName"[, replacingValue]);
. Poiché Interface
non ha una definizione per _$
, i valori non sono accessibili da oggetti esterni. Poiché ogni corpo di funzione prototipato this
è impostato values
sull'oggetto in funzione $
, si otterranno eccezioni se si chiamano direttamente funzioni di fratelli del costruttore; il modello _ $ / $ deve essere seguito anche nei corpi funzione prototipati. Sotto l'utilizzo del campione.
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");
E l'output della console.
classX1.valA is invalid.
classX1.valA: undefined
classX1.valA is valid.
classX1.valA: v1
classX2.valA: v2
Il modello _ $ / $ consente la totale privacy dei valori nelle classi completamente prototipate. Non so se lo userò mai, né se ha dei difetti, ma hey, è stato un bel puzzle!