sirve - ¿Cómo implementar los modificadores de acceso C#en javascript?
para que sirve el private en programacion (5)
Resumen
Traté de lograr la herencia y la encapsulación correctamente en javascript como en un lenguaje basado en clases como c #.
La parte fea es que los miembros protegidos tienen múltiples copias en las instancias privadas a las que solo se puede acceder mediante el cierre, y no tengo una idea, excepto refrescar esos miembros a las instancias privadas.
Si es posible, quiero deshacerme tanto de la
transmit
como de latransfer
en mi código deFunction.extend
.Actualización Para las personas que están interesadas en citar o investigar, aquí está el repositorio de código fuente:
La historia
Como los ensamblados pueden ser un concepto que está fuera del alcance de javascript, no tomo en cuenta el modificador
internal
, sinopublic
,protected
yprivate
.public
modificadorespublic
yprivate
no son tan difíciles de lograr; pero con herencia,protected
es significativamente complicado. Sin embargo, no es algo recomendable para hacer con javascript, la mayoría de los artículos que he leído dicen el prefijo con un carácter especial y lo documentan .Pero parece que persisto en hacer javascript para simular idiomas basados en clases. Robé esta idea e implementé en mi camino, el código está en la parte posterior de esta publicación.
La idea detrás de la escena es poner mayor accesibilidad con un prototipo más alto y acceder al más alto con un cierre.
Digamos que tenemos tres prototipos
A
,D
yG
, parece queComo no es posible que un objeto sea una instancia de un tipo también de otro tipo que no esté en la cadena de prototipos; la forma que elegí es encadenar el nivel
protected
horizontalmente y copiar los miembros del prototipo del tipo declarante. Esto hace posible la clase de anidamiento, porque los miembros declarados en un tipo menos derivado pueden propagarse a tipos más derivados; el método detransmit
en mi código es hacer esto. SiA
,D
yG
tienen sus propios miembros protegidos, se vería así:El cierre para acceder a la instancia privada es
this['''']
. Se necesita un argumento que sea para identificar una clase. El titular de modificadores es solo el identificador de clase, llamadoy
enFunction.extend
y_
en el código de prueba, no debe exponerse fuera de la declaración de clase. También se usa como atajo dethis['''']
._[''base'']
no es solo el invocador del constructor base, sino también el creador de las instancias privadas. Crea las instancias privadas y actualizathis['''']
para cada constructor con la herencia, por lo que siempre debe invocarse en los constructores.Aunque una instancia privada tendría el acceso de los miembros públicos, no debería usarse para modificarlos, ya que no se garantiza que
this['''']
se invoque cuando se accede a miembros públicos. Pero el acceso a la instancia privada es;recent
recuerda la instancia privada accedida más recientemente y actualiza los miembros protegidos si hay cambios.Mi pregunta es, ¿cómo puedo deshacerme de este tipo de actualización de los miembros protegidos? ¿Hay mejores ideas para lograr la encapsulación más realista?
pd: en realidad, no quiero una solución que use métodos / propiedades no estándar ... y sería mejor que haya polyfills si los métodos / propiedades usados son demasiado fashion para los navegadores antiguos.
Function.extend
Function.extend=function (base, factory) { factory.call(initializeClass); updateStaticMembersOfDerivedInnerClasses(y[''public''].constructor); transfer(y[''protected''], y[''public'']); return y[''public''].constructor; function y($this) { return $this[''''](y); } function updateStaticMembersOfDerivedInnerClasses(outer) { var member, inner; for (var key in outer) { if (Object.prototype.hasOwnProperty.call(outer, key)? (member=outer[key]) instanceof outer? outer!==(inner=member.constructor): false:false) { transfer(inner, outer); } } } function initializeInstance() { var $this=Object.create(y[''private'']); var derivedGet=this['''']; var recent=$this; this['''']=function (x) { var value=y!==x?derivedGet.call(this, x):$this; if (value!==recent) { transfer(value, recent, x[''protected'']); recent=value; } return value; }; base.apply(this, arguments); $this['''']=this['''']; } function initializeClass(derived) { y[''public'']=Object.create(base.prototype); y[''public''].constructor=derived; if (Object.prototype.hasOwnProperty.call(base, ''transmit'')) { base.transmit(y); } else { y[''protected'']=Object.create(y[''public'']); } y[''private'']=Object.create(y[''protected'']); y[''base'']=initializeInstance; transfer(derived, base); derived.transmit=function (x) { if (x[''public''] instanceof derived) { x[''protected'']=Object.create(y[''protected'']); x[''protected''].constructor=x[''public''].constructor; } }; derived.prototype=y[''public'']; return y; } };
transferir
function transfer(target, source, descriptor) { if (target!==source? ''undefined''!==typeof target? ''undefined''!==typeof source: false:false) { var keys=''undefined''!==typeof descriptor?descriptor:source; for (var key in keys) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key]=source[key]; } } } }
código de prueba
''use strict''; var BaseClass=Function.extend(Object, function () { var _=this(BaseClass); var NestedClass=Function.extend(BaseClass, function () { var _=this(NestedClass); function NestedClass(x, y, z) { _[''base''].apply(this, arguments); _(this).Y=y; _(this).Z=z; } _[''public''].SetX=function (x) { _(this).InternalSetX(x); }; _[''public''].GetX=function () { return _(this).InternalGetX(); }; _[''public''].GetY=function () { return _(this).Y; }; _[''public''].SetZ=function (z) { _(this).Z=z; }; _[''public''].GetZ=function () { return _(this).Z; }; _[''private''].Y=0; }); function BaseClass(x) { _[''base''].apply(this, arguments); _(this).X=x; } _[''protected''].InternalSetX=function (x) { _(this).X=x; }; _[''protected''].InternalGetX=function () { return _(this).X; }; _[''private''].X=0; _[''protected''].Z=0; BaseClass.Sample=new NestedClass(1, 2, 3); }); var DerivedClass=Function.extend(BaseClass, function () { var _=this(DerivedClass); function DerivedClass(x, y, z) { _[''base''].apply(this, arguments); } }); var o=DerivedClass.Sample; alert(o.GetX()); alert(o.GetY()); alert(o.GetZ()); o.SetX(3); o.SetZ(1); alert(o.GetX()); alert(o.GetY()); alert(o.GetZ());
He estado trabajando en otro proyecto de JavaScript interesante e implementado algo que puede estar más cerca de lo que estás buscando.
Interesado en tus pensamientos
Javascript es un lenguaje amplio, porque puedes hacer casi todas las cosas que quieres en una página web, simplemente creando funciones y encontrando formas de hacerlo.
Puedo decirte que JavaScript no es un lenguaje seguro, porque puedes acceder fácilmente a la mayoría de las variables y funciones, leerlas y saber cómo funciona, simplemente accediendo al archivo .js, incluido en la página.
Mi idea: algunos modificadores de acceso no se crearon para usar en JavaScript debido a que los desarrolladores ya saben que tal vez sea inútil, porque JavaScript no "viaja" a otros lugares (páginas), a menos que use una variable de sesión.
Y sobre los modificadores:
Privado
Protegido
- Público
Puedo decirte que conozco algunos modificadores de JavaScript que tienen alguna similitud con ellos, que son:
Local:
var Variable = 0;
Automáticamente, esto se convierte en una variable entera, porque está recibiendo un valor entero, y también, esta es una variable LOCAL debido al modificador var
que declara esta variable de manera que no se puede acceder al valor de la misma, a menos que usted están dentro de la misma función que esta variable fue declarada.
Ejemplo:
Si declaras estas funciones de esta manera, con modificadores predeterminados:
function conflict(){
i = 2;
changeI();
alert(i);
}
function changeI(){
i = 0;
}
En este caso, la i
es la misma variable para las dos funciones.
Entonces, si ejecutas conflict();
obtendrá una alerta que resultará 0
.
PERO, si declaras que estoy usando el modificador de var
:
function conflict(){
var i = 2;
changeI();
alert(i);
}
function changeI(){
var i = 0;
}
En este caso, tiene dos variables i
, porque están restringidas para usar solo dentro de su función, por lo tanto, si ejecuta conflict();
ahora, recibirá una alerta con valor de 2
.
Variable de clase:
this.Variable = "a";
Esta variable es automáticamente una Cadena, porque está recibiendo un valor de Cadena. Probablemente ya sepa lo que hace this
modificador, pero trataré de explicarlo desde mi punto de vista, es decir, que esta variable proviene de la Superclase o en javascript a "SuperFunction" que se puede llamar una clase, o en otras palabras, la clase "padre".
Un ejemplo:
function TClass()
{
this.getVar = function()
{
try
{
return "test";
}
catch(err)
{
return false;
}
}
this.alertVar = function()
{
try
{
alert(this.getVar());
}
catch(err)
{
alert(''error'');
}
}
}
var $Class = new TClass();
Como puede ver arriba, creé un Class TClass
y algunas variables que contienen funciones en él (javascript closure) y agregué el modificador this.
para ellos, para hacerlos vinculados al TClass
y como se ve en la función alertVar()
, alertVar()
a la alert(this.getVar());
la función que son del TClass
que es igual a this
en este contexto.
Y esta parte: var $Class = new TClass();
Estoy creando la clase como probablemente sabías, para tener acceso a sus métodos, haciendo esto puedo ejecutar, probar:
$Class.alertVar();
Y obteniendo como resultado una alerta que contiene "prueba", como puede ver:
Tenga en cuenta que no puede acceder a los métodos de TClass
de otras maneras, solo puede acceder a él creando la clase y accediendo a ella.
Así que espero que hayas entendido la usabilidad del modificador.
Global:
window.Variable = true;
Automáticamente, javascript declara que esta variable es booleana, porque está recibiendo un valor booleano. El modificador de window
como dice, puedes acceder a él sea lo que sea que estés en la ventana que eres, porque las variables de JavaScript cuando se declaran, van al DOM en la ventana, mira lo que es DOM:
DOM (Document Object Model): DOM, es una plataforma múltiple que representa cómo se organizan y se leen los marcadores html, xhtml y xml en el navegador que está utilizando. En otras palabras, si accede al DOM, puede ver todas las propiedades, todas las variables o cosas que existen en el navegador en este momento.
A diferencia de otras variables, las variables de la window
pueden tener asignado otro valor y acceder al valor real, independientemente de lo que sea, dentro o fuera de una función, dentro de un archivo js o no.
Ejemplo de Global (ventana):
Ejecute en el evento onLoad
de una página un código que declare una variable de window
, o explíquelo usted mismo usando la consola del navegador:
window.Variable = true;
Luego, agregue un archivo JS que contenga esta función, o créelo usted mismo simplemente ejecutando el código en la consola del navegador:
function testGlobalVar(){
if (Variable)
alert("it works!");
}
Cuando ejecutas testGlobalVar()
obtendrás la alerta, pero es solo porque lo declaraste como ''ventana''; de lo contrario, no obtendrás nada.
Modificador predeterminado:
Variable = 0.5
Automáticamente, esta variable se declara flotante porque recibe un valor Float. No sé si ya lo sabes, pero las variables de JavaScript declaradas de la manera habitual tienen un modificador predeterminado que hace que la variable sea similar a las variables de window
, pero no puedes acceder a ella desde lo que sea, pero en la mayoría de los casos puedes acceder a ella, particularmente , no conozco todos los casos en los que no se puede acceder, pero sé que no se puede cuando cargó un archivo js y se declaró dentro de él. Solo si ejecuta una función que lo declara, y luego intenta acceder.
Por cierto, veo que quieres conocer modificadores que coincidan con los tres que dijiste, pero en mi opinión algunos de los modificadores que te dije pueden usarse para hacer lo mismo que tus modificadores c #.
Espero que entiendas lo que estoy diciendo.
Ah, y si estaba confundido cuando vio una función dentro de una variable, estudie Javascript Closures, entenderá después de eso :).
Si bien el código con cierre podría resolver lo que usted desea, utilizaría los métodos Privileged más simples como los llamó Crockford aquí .
La idea de uso es simple:
- Defina el método privilegiado en el objeto base (con el límite 1 - permite ser llamado solo una vez).
- El método privilegiado devuelve una interfaz protegida de sí mismo (de un objeto base) que contiene funciones protegidas (probablemente esas funciones están definidas de forma privada en la base y luego se copian en el objeto de interfaz protegido ... o tal vez exista una interfaz protegida) en privado).
- Cada objeto extiende su interfaz protegida con la interfaz protegida de su objeto base y aún la expone a través del método privilegiado.
Terminarás con algo como esto:
function A() {
var protected = {
protectedA: function() { }
};
this.getProtected = (function() {
var allow = true;
//privileged function.
return function() {
if (allow) {
allow = false;
return protected;
}
};
});
}
//B - derives from (extends) A
function B() {
var base = {}; //acquiring a base omitted - depends on your implementation.
var protected = {
protectedB: function() { }
};
//"extend" simply copies new members into protected:
protected = $.extend(protected, base.getProtected());
this.getProtected = function() {
/* privileged function - similar to A.getProtected */
};
}
JavaScript tiene capacidades limitadas en esta medida, por lo que el azúcar protected
tiene algún costo de todos modos.
También tuve un pensamiento similar y decidí intentar escribir algo. Una solución vanilla js. Aún temprano pero me gusta lo que salió de eso. Es posible que también lo encuentre interesante.
No es exactamente c # pero proporciona un ecosistema más estricto. Y algunas otras funciones avanzadas de js en una solución liviana.
https://github.com/iamlothian/rucksack.js
Esta no es una solución para su código, sino una solución a su concepto. Si su objetivo era lograr que su idea funcione, continúe de todos modos, ya que estoy interesado en el resultado.
Si me gusta solo quiero un entorno js más estructurado, entonces aquí hay uno que escribí con una ambición similar a sus conceptos de preguntas.
Parte 2:
La idea aquí es usar restricciones de cierre y acceso para crear un patrón que restrinja la forma en que se puede usar y cambiar el código una vez que se ha definido. En su mayor parte, se ha hecho mucho trabajo duro. Pero el patrón se deja para que usted defina.
Aquí hay un ejemplo rápido y simulado que demuestra cómo puede implementar una herencia pública | protegida | privada. Estoy tratando de decidir si el clima implemento algo de esto como una función incorporada o dejo que los usuarios implementen su propia extensión de objeto como lo hice en el ejemplo.
http://plnkr.co/edit/ao2hTyBV1b3nYIwr7ZS5
La implementación está en scripts.js. ver tu consola para ver qué está pasando.
Lo que proporciona la mochila es un marco para crear módulos separados de código. Estos módulos están agrupados en espacios de nombres y pueden depender el uno del otro. Estas dependencias se resuelven perezosamente según lo definido, por lo que el orden de definición no es realmente importante. El proceso de resolución proporciona algunas otras funciones útiles, como el emparejamiento de interfaces y el módulo sellado.
características actuales:
- Modular
- Inyección de dependencia
- Factory constructor (Instances Object)
- Constructor de servicios (objetos estáticos)
- Carga lenta
- Fácil registro de errores (todos los errores dentro de los módulos se capturan y pueden transmitirse)
- Espacios de nombres
- Módulos y espacios de nombres sellables (módulos a los que no se puede acceder desde fuera del espacio de nombres)
- Global aguarda evento para el módulo
- Interfaz para el objeto config opcional
- Interfaz estricta opcional para la inyección
Cómo las clases de padres e hijos interactúan entre sí
Una clase secundaria extendida llama a
super.call
, una función que construye una instancia de su padre.Una clase principal comparte sus miembros protegidos (campos y funciones) con la subclase de ampliación utilizando
this.share
en su constructor.Una subclase también puede llamar a
super.fetch()
, que devuelve el objeto de los campos / funciones que la clase principal pasó athis.share
Para ilustrar mi técnica, el siguiente código demuestra algunos elementos esenciales de OOP con un ejemplo simple de una class Dog extends Animal
Algunas funciones básicas para este modelo orientado a objetos
// runs in both node.js and browser
var global_namespace = (''undefined''==typeof module)? window: global;
// put a no-operation function in the value for `share` in case nothing is extending a class
var not_extendable = {share:function(){}};
// when something is extending a class...
var extendable = function(constructor) {
// create a space for protected fields
var protected_space = {};
// the following is what will get passed as `this` to the parent constructor
var sharing = {
share: function(fields) {
protected_space = fields;
},
};
// the following is what will get passed as the first arg to the child constructor
return {
// enables child to call its parent''s constructor
call: function(args) {
return constructor.apply(sharing, args);
},
// allows child to access protected fields shared by its parent
fetch: function() {
return protected_space;
},
};
};
Clase de Animal
// class definition for `Animal`
(function(namespace) {
// construct an instance of this class
var constructor = function(name, weight, info) {
// private fields
var color = (info && info.color) || ''unknown'';
// for protected fields
var protect = {
weight: weight,
noise: function() {
return ''nothing'';
},
};
// share the protected fields with any subclass that might be extending this
this.share(protect);
// public fields and methods
return {
speak: function() {
console.log(name+'' says ''+protect.noise());
},
feed: function() {
console.log(name+'' is not hungry'');
},
weigh: function() {
console.log(name+'' weighs ''+protect.weight+'' lbs'');
},
toString: function() {
return ''{Animal}'';
},
};
};
// handle calls to: `Animal()`
namespace.Animal = function() {
// called with new operator: `new Animal(...)`
if(this !== namespace) {
// construct simple instance of this class
return constructor.apply(not_extendable, arguments);
}
// static call: `Animal(...)`, means the caller wants to extend this class
else {
// reference child constructor
var child_constructor = arguments[0];
// return a wrapped constructor function
return function() {
// call child constructor and allow it to call the super constructor
return child_constructor.apply({}, [extendable(constructor), arguments]);
};
}
};
})(global_namespace);
Clase de Dog
// class definition for `Dog`
(function(namespace) {
// class `Dog` extends class `Animal`
var constructor = Animal(function(super_class, args) {
// private field
var been_fed = false;
// call super''s constructor
var operator = super_class.call(args);
// inherit parent''s protected members
var parent = super_class.fetch();
// override a protected method
parent.noise = function() {
return ''bark!'';
};
// override a public method
operator.feed = function() {
been_fed = true;
parent.weight += 5;
};
// extend a public method
var super_weigh = operator.weigh;
operator.weigh = function() {
super_weigh();
if(been_fed) console.log(''/t''+args[0]+'' has been eating :)'');
else console.log(''/t''+args[0]+'' has not been fed yet'');
};
// override another public method
operator.toString = function() {
return ''{Dog}'';
},
// return the operator (interfacable instance object)
return operator;
});
// handle calls to: `Dog()`
namespace.Dog = function() {
// called with new operator: `new Dog()`
if(this !== namespace) {
return constructor.apply(this, arguments);
}
// static call: `Dog()`
else {
// we do no allow extending class `Dog`
return false;
}
};
})(global_namespace);
Entonces, ahora, podemos hacer esto:
var giraffe = new Animal(''Mr. Giraffe'', 720);
giraffe.speak(); // "Mr. Giraffe says nothing"
giraffe.weigh(); // "Mr. Giraffe weighs 720 lbs"
var buddy = new Dog(''Buddy'', 50);
buddy.speak(); // "Buddy says bark!"
buddy.weigh(); // "Buddy weighs 50 lbs"
// "Buddy has not been fed yet"
buddy.feed();
buddy.weigh(); // "Buddy weighs 55 lbs"
// "Buddy has been eating :)"
Esto permite campos / funciones privadas, protegidas y públicas. Los campos / funciones protegidos y públicos pueden sobrescribirse y ampliarse.
console.log(giraffe); // "{Animal}"
console.log(buddy); // "{Dog}"