Я знаю, что прошло более 1 десятилетия с тех пор, как об этом спросили, но я просто размышлял над этим n-й раз в своей жизни программиста и нашел возможное решение, которое мне пока неизвестно, нравится ли мне еще , Я не видел этой методологии, документированной ранее, поэтому я назову ее «паттерн частного / публичного доллара» или паттерн _ $ / $ .
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]);
Концепция использует функцию ClassDefinition, которая возвращает функцию Constructor, которая возвращает объект Interface . Единственный метод интерфейса - $
это получение name
аргумента для вызова соответствующей функции в объекте конструктора, любые дополнительные аргументы, переданные после name
, передаются в вызове.
Глобально определена вспомогательная функция ClassValues
сохраняет все поля в качестве объекта по мере необходимости. Он определяет _$
функцию доступа к ним name
. Это следует за коротким шаблоном get / set, поэтому, если value
он пройден, он будет использоваться как новое значение переменной.
var ClassValues = function (values) {
return {
_$: function _$(name, value) {
if (arguments.length > 1) {
values[name] = value;
}
return values[name];
}
};
};
Глобально определенная функция Interface
принимает объект и Values
объект для возврата _interface
с одной единственной функцией, $
которая исследует, obj
чтобы найти функцию, названную в честь параметра, name
и вызывает ее values
как объект области видимости . Переданные дополнительные аргументы $
будут переданы при вызове функции.
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;
};
В приведенном ниже примере ClassX
присваивается результат ClassDefinition
, который является Constructor
функцией. Constructor
может получить любое количество аргументов. Interface
это то, что внешний код получает после вызова конструктора.
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;
}());
Нет смысла иметь функции, не являющиеся прототипами Constructor
, хотя вы можете определить их в теле функции конструктора. Все функции вызываются по общедоступной схеме доллара this.$("functionName"[, param1[, param2 ...]])
. Доступ к частным значениям осуществляется по схеме частного доллара this._$("valueName"[, replacingValue]);
. Поскольку Interface
нет определения для _$
, значения не могут быть доступны для внешних объектов. Поскольку тело каждого прототипа функции this
установлено на values
объект в функции $
, вы получите исключения, если вызовете функции-братья Constructor напрямую; в _ $ / $ шаблон должен сопровождаться в функции органов прототип тоже. Ниже пример использования.
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");
И консольный вывод.
classX1.valA is invalid.
classX1.valA: undefined
classX1.valA is valid.
classX1.valA: v1
classX2.valA: v2
_ $ / $ Шаблон обеспечивает полную конфиденциальность значений в полностью прототипов классов. Я не знаю, буду ли я когда-нибудь этим пользоваться, и не будет ли у него недостатков, но это была хорошая головоломка!