Sei que já faz mais de uma década que isso foi perguntado, mas acabei de pensar nisso pela enésima vez na vida de programador e encontrei uma possível solução que ainda não sei se ainda gosto totalmente. . Eu não vi essa metodologia documentada antes, então vou chamá-la de "padrão de dólar público / privado" ou _ $ / $ .
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]);
O conceito usa uma função ClassDefinition que retorna uma função Constructor que retorna um objeto Interface . O único método da interface é o $
que recebe um name
argumento para chamar a função correspondente no objeto construtor, quaisquer argumentos adicionais passados apósname
são passados na invocação.
A função auxiliar definida globalmente ClassValues
armazena todos os campos em um objeto, conforme necessário. Ele define a _$
função para acessá-los name
. Isso segue um padrão curto de obtenção / configuração, portanto, se value
for passado, ele será usado como o novo valor da variável.
var ClassValues = function (values) {
return {
_$: function _$(name, value) {
if (arguments.length > 1) {
values[name] = value;
}
return values[name];
}
};
};
A função definida globalmente Interface
pega um objeto e um Values
objeto para retornar um _interface
com uma única função $
que examina obj
para encontrar uma função nomeada após o parâmetro name
e a invoca values
como o objeto com escopo definido . Os argumentos adicionais passados para $
serão passados na chamada de função.
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;
};
Na amostra abaixo, ClassX
é atribuído o resultado de ClassDefinition
, qual é a Constructor
função. Constructor
pode receber qualquer número de argumentos. Interface
é o que o código externo obtém após chamar o construtor.
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;
}());
Não faz sentido ter funções não prototipadas Constructor
, embora você possa defini-las no corpo da função construtora. Todas as funções são chamadas com o padrão de dólar público this.$("functionName"[, param1[, param2 ...]])
. Os valores privados são acessados com o padrão de dólar privado this._$("valueName"[, replacingValue]);
. Como Interface
não existe uma definição para _$
, os valores não podem ser acessados por objetos externos. Como o corpo de cada função prototipada this
é definido como o values
objeto em função $
, você receberá exceções se chamar diretamente as funções irmãos do Constructor; o padrão _ $ / $ também precisa ser seguido nos corpos das funções prototipadas. Abaixo o uso da amostra.
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 a saída do console.
classX1.valA is invalid.
classX1.valA: undefined
classX1.valA is valid.
classX1.valA: v1
classX2.valA: v2
O padrão _ $ / $ permite total privacidade de valores em classes totalmente prototipadas. Não sei se vou usar isso, nem se tem falhas, mas, ei, foi um bom quebra-cabeça!