Wiem, że minęła ponad dekada, odkąd o to poproszono, ale po prostu zastanowiłem się nad tym po raz n-ty w życiu programisty i znalazłem możliwe rozwiązanie, którego nie wiem, czy całkowicie mi się podoba . Nie widziałem wcześniej tej metodologii, więc nadam jej nazwę „prywatny / publiczny wzór dolara” lub _ $ / $ $ .
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]);
Koncepcja wykorzystuje funkcję ClassDefinition, która zwraca funkcję konstruktora, która zwraca obiekt interfejsu . Jedyną metodą interfejsu jest $
otrzymanie name
argumentu w celu wywołania odpowiedniej funkcji w obiekcie konstruktora, a wszelkie dodatkowe argumenty przekazane później name
są przekazywane w wywołaniu.
Globalnie zdefiniowanej funkcji pomocnika ClassValues
przechowuje wszystkie pola z obiektu w razie potrzeby. Określa _$
funkcję dostępu do nich name
. Jest to zgodne z krótkim wzorcem get / set, więc jeśli value
zostanie przekazany, zostanie użyty jako nowa wartość zmiennej.
var ClassValues = function (values) {
return {
_$: function _$(name, value) {
if (arguments.length > 1) {
values[name] = value;
}
return values[name];
}
};
};
Globalnie zdefiniowana funkcja Interface
pobiera obiekt i Values
obiekt _interface
z jedną funkcją, $
która sprawdza, czy obj
znaleźć funkcję nazwaną na podstawie parametru name
i wywołuje ją values
jako obiekt o zasięgu . Dodatkowe przekazane argumenty $
zostaną przekazane przy wywołaniu funkcji.
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;
};
W poniższym przykładzie ClassX
jest przypisany do wyniku ClassDefinition
, który jest Constructor
funkcją. Constructor
może otrzymać dowolną liczbę argumentów. Interface
to, co otrzymuje kod zewnętrzny po wywołaniu konstruktora.
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;
}());
Nie ma sensu mieć funkcji nie prototypowych Constructor
, chociaż można je zdefiniować w treści funkcji konstruktora. Wszystkie funkcje są wywoływane z publicznym wzorem dolara this.$("functionName"[, param1[, param2 ...]])
. Dostęp do prywatnych wartości uzyskuje się za pomocą prywatnego wzoru dolara this._$("valueName"[, replacingValue]);
. Ponieważ Interface
nie ma definicji _$
, do obiektów zewnętrznych nie można uzyskać dostępu do wartości. Ponieważ każde prototypowane ciało funkcji this
jest ustawione na values
obiekt w funkcji $
, otrzymasz wyjątki, jeśli bezpośrednio wywołasz funkcje rodzeństwa Konstruktora; te _ $ / $ wzór musi być następnie w organach prototypy funkcji też. Poniżej przykładowe użycie.
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");
I wyjście konsoli.
classX1.valA is invalid.
classX1.valA: undefined
classX1.valA is valid.
classX1.valA: v1
classX2.valA: v2
_ $ / $ Wzór pozwala na pełną prywatność wartościami w pełni prototyp klas. Nie wiem, czy kiedykolwiek tego użyję, czy też ma wady, ale hej, to była dobra łamigłówka!