¿Hay alguna forma de hacer que las variables "privadas" (las definidas en el constructor) estén disponibles para los métodos definidos por el prototipo?
TestClass = function(){
var privateField = "hello";
this.nonProtoHello = function(){alert(privateField)};
};
TestClass.prototype.prototypeHello = function(){alert(privateField)};
Esto funciona:
t.nonProtoHello()
Pero esto no:
t.prototypeHello()
Estoy acostumbrado a definir mis métodos dentro del constructor, pero me estoy alejando de eso por un par de razones.
javascript
private-members
Morgangandes
fuente
fuente
Respuestas:
No, no hay forma de hacerlo. Eso sería esencialmente un alcance inverso.
Los métodos definidos dentro del constructor tienen acceso a variables privadas porque todas las funciones tienen acceso al alcance en el que se definieron.
Los métodos definidos en un prototipo no están definidos dentro del alcance del constructor y no tendrán acceso a las variables locales del constructor.
Todavía puede tener variables privadas, pero si desea que los métodos definidos en el prototipo tengan acceso a ellas, debe definir captadores y definidores en el
this
objeto, a los que tendrán acceso los métodos prototipo (junto con todo lo demás) . Por ejemplo:fuente
secret
una propiedad dethis
. JavaScript simplemente no admite variables privadas con prototipos, ya que los prototipos están vinculados al contexto del sitio de llamada, no al contexto del 'sitio de creación'.person.getSecret()
entonces?Actualización: con ES6, hay una mejor manera:
En pocas palabras, puede usar el nuevo
Symbol
para crear campos privados.Aquí hay una gran descripción: https://curiosity-driven.org/private-properties-in-javascript
Ejemplo:
Para todos los navegadores modernos con ES5:
Puedes usar solo cierres
La forma más sencilla de construir objetos es evitar por completo la herencia de prototipos. Simplemente defina las variables privadas y las funciones públicas dentro del cierre, y todos los métodos públicos tendrán acceso privado a las variables.
O puede usar solo prototipos
En JavaScript, la herencia prototípica es principalmente una optimización . Permite que varias instancias compartan métodos prototipo, en lugar de que cada instancia tenga sus propios métodos.
El inconveniente es que
this
es lo único diferente cada vez que se llama a una función prototípica.Por lo tanto, cualquier campo privado debe ser accesible a través de
this
, lo que significa que serán públicos. Así que nos limitamos a las convenciones de nomenclatura para_private
campos.No te molestes en mezclar cierres con prototipos
Creo que no deberías mezclar variables de cierre con métodos prototipo. Deberías usar uno u otro.
Cuando utiliza un cierre para acceder a una variable privada, los métodos prototipo no pueden acceder a la variable. Entonces, tienes que exponer el cierre
this
, lo que significa que lo está exponiendo públicamente de una forma u otra. Hay muy poco que ganar con este enfoque.¿Cuál elijo?
Para objetos realmente simples, solo use un objeto simple con cierres.
Si necesita una herencia prototípica, por herencia, rendimiento, etc., entonces cumpla con la convención de nomenclatura "_private" y no se moleste con los cierres.
No entiendo por qué los desarrolladores de JS se esfuerzan TAN por hacer que los campos sean realmente privados.
fuente
_private
convención de nomenclatura sigue siendo la mejor solución si desea aprovechar la herencia de prototipos.Symbol
, que es una excelente manera de crear campos privados. Aquí hay una gran explicación: curiosity-driven.org/private-properties-in-javascriptSymbol
cierre en una clase que abarque toda su clase. De esa manera, todos los métodos prototipo pueden usar el Símbolo, pero nunca se expone fuera de la clase.Object.getOwnPropertySymbols
. Entonces esto es solo privacidad por oscuridad.toString
. Esto no es diferente de Java o C # ... todavía se puede acceder a los miembros privados a través de la reflexión, pero generalmente están muy ocultos. Todo lo cual sirve para fortalecer mi punto final: "No entiendo por qué los desarrolladores de JS se esfuerzan TAN por hacer que los campos sean realmente privados".Cuando leí esto, sonaba como un desafío difícil, así que decidí encontrar la manera. Lo que se me ocurrió fue CRAAAAZY pero funciona totalmente.
Primero, intenté definir la clase en una función inmediata para que tuviera acceso a algunas de las propiedades privadas de esa función. Esto funciona y le permite obtener algunos datos privados, sin embargo, si intenta establecer los datos privados, pronto encontrará que todos los objetos compartirán el mismo valor.
Hay muchos casos en los que esto sería adecuado, como si quisiera tener valores constantes como nombres de eventos que se comparten entre instancias. Pero esencialmente, actúan como variables privadas estáticas.
Si absolutamente necesita acceso a variables en un espacio de nombres privado desde sus métodos definidos en el prototipo, puede probar este patrón.
Me encantaría recibir comentarios de cualquiera que vea un error con esta forma de hacerlo.
fuente
i
se ha agregado a todas las instancias. Por lo tanto, no es completamente "transparente" yi
aún podría ser manipulado.ver la página de Doug Crockford sobre esto . Debe hacerlo indirectamente con algo que pueda acceder al alcance de la variable privada.
otro ejemplo:
caso de uso:
fuente
_set
a través deset
? ¿Por qué no solo nombrarloset
para empezar?Sugiero que probablemente sería una buena idea describir "tener una asignación de prototipo en un constructor" como un antipatrón Javascript. Piénsalo. Es demasiado arriesgado.
Lo que realmente está haciendo allí al crear el segundo objeto (es decir, b) es redefinir esa función prototipo para todos los objetos que usan ese prototipo. Esto restablecerá efectivamente el valor para el objeto a en su ejemplo. Funcionará si desea una variable compartida y si crea todas las instancias de objetos por adelantado, pero se siente demasiado arriesgado.
Encontré un error en algunos Javascript en los que estaba trabajando recientemente debido a este antipatrón exacto. Intentaba establecer un controlador de arrastrar y soltar en el objeto particular que se estaba creando, pero en cambio lo hacía para todas las instancias. No está bien.
La solución de Doug Crockford es la mejor.
fuente
@Kai
Eso no funcionará. Si lo haces
luego
t2.prototypeHello
accederá a la sección privada de t.@AnglesCrimes
El código de muestra funciona bien, pero en realidad crea un miembro privado "estático" compartido por todas las instancias. Puede que no sea la solución que buscaban los morgancodes.
Hasta ahora no he encontrado una manera fácil y limpia de hacer esto sin introducir un hash privado y funciones de limpieza adicionales. Una función de miembro privado se puede simular en cierta medida:
fuente
privateFoo
es completamente privado y, por lo tanto, invisible al obtener unnew Foo()
. Solobar()
es un método público aquí, al que tiene accesoprivateFoo
. Puede usar el mismo mecanismo para variables y objetos simples, sin embargo, siempre debe tener en cuenta que estosprivates
son realmente estáticos y serán compartidos por todos los objetos que cree.Si es posible. El patrón de diseño PPF solo resuelve esto.
PPF son las siglas de Private Prototype Functions. Basic PPF resuelve estos problemas:
Para el primero, solo:
Es así de simple. Por ejemplo:
...
Lee la historia completa aquí:
Patrón de diseño PPF
fuente
En realidad, puede lograr esto utilizando la Verificación de Accesor :
Este ejemplo proviene de mi publicación sobre Funciones prototípicas y datos privados y se explica con más detalle allí.
fuente
En JavaScript actual, estoy bastante seguro de que hay una y solo una forma de tener un estado privado , accesible desde funciones prototipo , sin agregar nada público a
this
. La respuesta es usar el patrón de "mapa débil".Para resumir: la
Person
clase tiene un solo mapa débil, donde las claves son las instancias de Person, y los valores son objetos simples que se utilizan para el almacenamiento privado.Aquí hay un ejemplo completamente funcional: (jugar en http://jsfiddle.net/ScottRippey/BLNVr/ )
Como dije, esta es realmente la única forma de lograr las 3 partes.
Sin embargo, hay dos advertencias. Primero, esto cuesta rendimiento: cada vez que accede a los datos privados, es una
O(n)
operación, donden
está el número de instancias. Por lo tanto, no querrá hacer esto si tiene una gran cantidad de instancias. En segundo lugar, cuando haya terminado con una instancia, debe llamardestroy
; de lo contrario, la instancia y los datos no se recolectarán basura, y terminará con una pérdida de memoria.Y es por eso que mi respuesta original, "No deberías" , es algo a lo que me gustaría apegarme.
fuente
Hay una manera más simple al aprovechar el uso de
bind
ycall
métodos.Al establecer variables privadas en un objeto, puede aprovechar el alcance de ese objeto.
Ejemplo
Este método no está exento de inconvenientes. Dado que el contexto del alcance se anula de manera efectiva, no tiene acceso fuera del
_private
objeto. Sin embargo, no es imposible dar acceso al alcance del objeto de instancia. Puede pasar el contexto del objeto (this
) como segundo argumentobind
aocall
para tener acceso a sus valores públicos en la función prototipo.Accediendo a valores públicos
fuente
¡Intentalo!
fuente
caller
, que es una extensión dependiente de la implementación no permitida en modo estricto.Esto es lo que se me ocurrió.
El principal problema con esta implementación es que redefine los prototipos en cada instancia.
fuente
Hay una manera muy simple de hacer esto
Los prototipos de JavaScript son dorados.
fuente
SharedPrivate.prototype
quethis.constructor.prototype
es un gran problema para redefinir GETP y SETP varias veces ...Llego tarde a la fiesta, pero creo que puedo contribuir. Aquí, mira esto:
Llamo a este método patrón de acceso . La idea esencial es que tenemos un cierre , una clave dentro del cierre, y creamos un objeto privado (en el constructor) al que solo se puede acceder si tiene la clave .
Si está interesado, puede leer más sobre esto en mi artículo . Con este método, puede crear propiedades por objeto a las que no se puede acceder fuera del cierre. Por lo tanto, puede usarlos en constructor o prototipo, pero no en ningún otro lugar. No he visto este método usado en ningún lado, pero creo que es realmente poderoso.
fuente
¿No puedes poner las variables en un alcance más alto?
fuente
También puede intentar agregar el método no directamente en el prototipo, sino en la función de constructor como esta:
fuente
Aquí hay algo que se me ocurrió al tratar de encontrar la solución más simple para este problema, tal vez podría ser útil para alguien. Soy nuevo en JavaScript, por lo que podría haber algunos problemas con el código.
fuente
Enfrenté exactamente la misma pregunta hoy y después de elaborar la respuesta de primera clase de Scott Rippey, se me ocurrió una solución muy simple (en mi humilde opinión) que es compatible con ES5 y eficiente, también es seguro para el choque de nombres (usar _private parece inseguro) .
Probado con ringojs y nodejs. Estoy ansioso por leer tu opinión.
fuente
¿Cómo es esto? Usando un descriptor de acceso privado. Solo le permite obtener las variables, aunque no establecerlas, depende del caso de uso.
fuente
Tengo una solución, pero no estoy seguro de que no tenga fallas.
Para que funcione, debe usar la siguiente estructura:
Aquí está el código:
Cómo funciona esto es que proporciona una función de instancia "this.getPrivateFields" para acceder al objeto de variables privadas "privateFields", pero esta función solo devolverá el objeto "privateFields" dentro del cierre principal definido (también funciones prototipo que usan "this.getPrivateFields" "debe definirse dentro de este cierre).
Un hash producido durante el tiempo de ejecución y difícil de adivinar se usa como parámetro para asegurarse de que incluso si se llama "getPrivateFields" fuera del alcance del cierre, no se devolverá el objeto "privateFields".
El inconveniente es que no podemos extender TestClass con más funciones prototipo fuera del cierre.
Aquí hay un código de prueba:
EDITAR: Usando este método, también es posible "definir" funciones privadas.
fuente
Estaba jugando con esto hoy y esta fue la única solución que pude encontrar sin usar Símbolos. Lo mejor de esto es que en realidad puede ser completamente privado.
La solución se basa en un cargador de módulos de cosecha propia que básicamente se convierte en el mediador de un caché de almacenamiento privado (usando un mapa débil).
fuente
Sé que ha pasado más de una década desde que me lo pidieron, pero solo pensé en esto por enésima vez en mi vida de programador, y encontré una posible solución que no sé si todavía me gusta por completo. . No he visto esta metodología documentada antes, así que la llamaré el "patrón de dólar público / privado" o el patrón _ $ / $ .
El concepto utiliza una función ClassDefinition que devuelve una función Constructor que devuelve un objeto Interface . El único método de la interfaz es el
$
que recibe unname
argumento para invocar la función correspondiente en el objeto constructor, cualquier argumento adicional pasado despuésname
se pasa en la invocación.La función auxiliar definida globalmente
ClassValues
almacena todos los campos en un objeto según sea necesario. Define la_$
función para acceder a ellosname
. Esto sigue un breve patrón get / set, por lo que sivalue
se pasa, se usará como el nuevo valor de la variable.La función definida globalmente
Interface
toma un objeto y unValues
objeto para devolver un_interface
con una sola función$
que examinaobj
para encontrar una función con el nombre del parámetroname
y la invocavalues
como el objeto con ámbito . Los argumentos adicionales pasados$
se pasarán en la invocación de la función.En la siguiente muestra,
ClassX
se asigna al resultado deClassDefinition
, que es laConstructor
función.Constructor
puede recibir cualquier cantidad de argumentos.Interface
es lo que obtiene el código externo después de llamar al constructor.No tiene sentido tener funciones no prototipadas
Constructor
, aunque podría definirlas en el cuerpo de la función del constructor. Todas las funciones se llaman con el patrón de dólar públicothis.$("functionName"[, param1[, param2 ...]])
. Se accede a los valores privados con el patrón de dólar privadothis._$("valueName"[, replacingValue]);
. ComoInterface
no tiene una definición para_$
, los objetos externos no pueden acceder a los valores. Dado que cada cuerpo de función prototipadathis
se establece en elvalues
objeto en función$
, obtendrá excepciones si llama directamente a las funciones hermanas Constructor; El patrón _ $ / $ también debe seguirse en los cuerpos de función prototipados. Debajo de la muestra de uso.Y la salida de la consola.
El patrón _ $ / $ permite la total privacidad de los valores en clases completamente prototipadas. No sé si alguna vez usaré esto, ni si tiene fallas, pero bueno, ¡fue un buen rompecabezas!
fuente
ES6 WeakMaps
Mediante el uso de un patrón simple basado en ES6, WeakMaps es posible obtener variables de miembros privados, accesibles desde las funciones prototipo .
Una explicación más detallada de este patrón se puede encontrar aquí.
fuente
Necesita cambiar 3 cosas en su código:
var privateField = "hello"
conthis.privateField = "hello"
.privateField
conthis.privateField
.privateField
conthis.privateField
.El código final sería el siguiente:
fuente
this.privateField
No sería un campo privado. es accesible desde el exterior:t.privateField
Puede usar una asignación de prototipo dentro de la definición del constructor.
La variable será visible para el método agregado del prototipo, pero todas las instancias de las funciones accederán a la misma variable COMPARTIDA.
Espero que esto pueda ser útil.
fuente