I know it has been more than 1 decade since this was was asked, but I just put my thinking on this for the n-th time in my programmer life, and found a possible solution that I don't know if I entirely like yet. I have not seen this methodology documented before, so I will name it the "private/public dollar pattern" or _$ / $ pattern.
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]);
The concept uses a ClassDefinition function that returns a Constructor function that returns an Interface object. The interface's only method is $ which receives a name argument to invoke the corresponding function in the constructor object, any additional arguments passed after name are passed in the invocation.
The globally-defined helper function ClassValues stores all fields in an object as needed. It defines the _$ function to access them by name. This follows a short get/set pattern so if value is passed, it will be used as the new variable value.
var ClassValues = function (values) {
return {
_$: function _$(name, value) {
if (arguments.length > 1) {
values[name] = value;
}
return values[name];
}
};
};
The globally defined function Interface takes an object and a Values object to return an _interface with one single function $ that examines obj to find a function named after the parameter name and invokes it with values as the scoped object. The additional arguments passed to $ will be passed on the function invocation.
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;
};
In the sample below, ClassX is assigned to the result of ClassDefinition, which is the Constructor function. Constructor may receive any number of arguments. Interface is what external code gets after calling the constructor.
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;
}());
There is no point in having non-prototyped functions in Constructor, although you could define them in the constructor function body. All functions are called with the public dollar pattern this.$("functionName"[, param1[, param2 ...]]). The private values are accessed with the private dollar pattern this._$("valueName"[, replacingValue]);. As Interface does not have a definition for _$, the values cannot be accessed by external objects. Since each prototyped function body's this is set to the values object in function $, you will get exceptions if you call Constructor sibling functions directly; the _$ / $ pattern needs to be followed in prototyped function bodies too. Below sample usage.
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");
And the console output.
classX1.valA is invalid.
classX1.valA: undefined
classX1.valA is valid.
classX1.valA: v1
classX2.valA: v2
The _$ / $ pattern allows full privacy of values in fully-prototyped classes. I don't know if I will ever use this, nor if it has flaws, but hey, it was a good puzzle!