¿Es posible crear propiedades privadas en las clases de ES6?
Aquí hay un ejemplo. ¿Cómo puedo evitar el acceso a instance.property
?
class Something {
constructor(){
this.property = "test";
}
}
var instance = new Something();
console.log(instance.property); //=> "test"
Respuestas:
Se están implementando campos privados (y métodos) en el estándar ECMA . Puede comenzar a usarlos hoy con preajuste de babel 7 y etapa 3.
fuente
this
en constructor antes de llamarsuper()
. Sin embargo, Babel los pone antes que Super.#privateCrap
sintaxis?#beep() {}
? y estoasync #bzzzt() {}
:?Respuesta corta, no, no hay soporte nativo para propiedades privadas con clases ES6.
Pero podría imitar ese comportamiento al no asociar las nuevas propiedades al objeto, sino al mantenerlas dentro de un constructor de clases, y utilizar captadores y definidores para alcanzar las propiedades ocultas. Tenga en cuenta que los getters y setters se redefinen en cada nueva instancia de la clase.
ES6
ES5
fuente
class
sintaxis en primer lugar.getName
y seansetName
privadas?Para ampliar la respuesta de @ loganfsmyth:
Los únicos datos verdaderamente privados en JavaScript siguen siendo las variables de ámbito. No puede tener propiedades privadas en el sentido de propiedades a las que se accede internamente de la misma manera que las propiedades públicas, pero puede usar variables de ámbito para almacenar datos privados.
Variables de alcance
El enfoque aquí es utilizar el alcance de la función constructora, que es privada, para almacenar datos privados. Para que los métodos tengan acceso a estos datos privados, también deben crearse dentro del constructor, lo que significa que los está recreando con cada instancia. Esta es una penalización de rendimiento y memoria, pero algunos creen que la penalización es aceptable. La penalización se puede evitar para métodos que no necesitan acceso a datos privados agregándolos al prototipo como de costumbre.
Ejemplo:
Scoak WeakMap
Se puede usar un WeakMap para evitar el rendimiento del enfoque anterior y la penalización de la memoria. WeakMaps asocia datos con objetos (aquí, instancias) de tal manera que solo se puede acceder usando ese WeakMap. Por lo tanto, usamos el método de variables con ámbito para crear un WeakMap privado, luego usamos ese WeakMap para recuperar datos privados asociados con
this
. Esto es más rápido que el método de variables con ámbito porque todas sus instancias pueden compartir un solo WeakMap, por lo que no necesita volver a crear métodos solo para que accedan a sus propios WeakMaps.Ejemplo:
Este ejemplo usa un objeto para usar un WeakMap para múltiples propiedades privadas; también puedes usar múltiples WeakMaps y usarlos como
age.set(this, 20)
, o escribir un pequeño contenedor y usarlo de otra manera, comoprivateProps.set(this, 'age', 0)
.La privacidad de este enfoque podría ser violada teóricamente al alterar el
WeakMap
objeto . Dicho esto, todo el JavaScript puede ser roto por los globals destrozados. Nuestro código ya está basado en el supuesto de que esto no está sucediendo.(Este método también podría hacerse
Map
, peroWeakMap
es mejor porqueMap
creará pérdidas de memoria a menos que sea muy cuidadoso, y para este propósito los dos no son diferentes).Media respuesta: símbolos con ámbito
Un símbolo es un tipo de valor primitivo que puede servir como nombre de propiedad. Puede usar el método de variable con ámbito para crear un símbolo privado y luego almacenar datos privados en
this[mySymbol]
.La privacidad de este método se puede violar usando
Object.getOwnPropertySymbols
, pero es algo incómodo de hacer.Ejemplo:
Media respuesta: guiones bajos
El valor predeterminado anterior es usar una propiedad pública con un prefijo de subrayado. Aunque no es una propiedad privada de ninguna manera, esta convención es lo suficientemente frecuente como para hacer un buen trabajo al comunicar que los lectores deben tratar la propiedad como privada, lo que a menudo hace el trabajo. A cambio de este lapso, obtenemos un enfoque que es más fácil de leer, más fácil de escribir y más rápido.
Ejemplo:
Conclusión
A partir de ES2017, todavía no hay una manera perfecta de hacer propiedades privadas. Varios enfoques tienen pros y contras. Las variables de ámbito son verdaderamente privadas; los WeakMaps con ámbito son muy privados y más prácticos que las variables con ámbito; Los símbolos de alcance son razonablemente privados y razonablemente prácticos; los guiones bajos suelen ser lo suficientemente privados y muy prácticos.
fuente
instanceof
. Admito que estaba pensando en ese enfoque como incluido solo por el bien de la integridad y debería haber pensado más en cuánto es realmente capaz de hacerlo.Actualización: una propuesta con una sintaxis más agradable está en camino. Las contribuciones son bienvenidas.
Sí, hay - para el acceso con alcance en los objetos - ES6 introduce
Symbol
s .Los símbolos son únicos, no puede obtener acceso a uno desde el exterior excepto con reflejo (como elementos privados en Java / C #), pero cualquiera que tenga acceso a un símbolo en el interior puede usarlo para acceder a las teclas:
fuente
Object.getOwnPropertySymbols
? ;)const myPrivateMethod = Math.random(); Something.prototype[''+myPrivateMethod] = function () { ... } new Something()[''+myPrivateMethod]();
. Esto no es realmente privacidad, es oscuridad, en el sentido del JavaScript tradicional. Consideraría JavaScript "privado" como el uso de cierres para encapsular variables. Por lo tanto, esas variables no son accesibles a través de la reflexión.private
yprotected
sería mucho más limpio queSymbol
oName
. Prefiero la notación de puntos en lugar de la notación de corchetes. Me gustaría seguir usando un punto para cosas privadas.this.privateVar
La respuesta es no". Pero puede crear acceso privado a propiedades como esta:
export
palabra clave.(La sugerencia de que los Símbolos podrían usarse para garantizar la privacidad era cierta en una versión anterior de la especificación ES6, pero ya no es así: https://mail.mozilla.org/pipermail/es-discuss/2014-January/035604. html y https://stackoverflow.com/a/22280202/1282216 . Para una discusión más larga sobre Símbolos y privacidad ver: https://curiosity-driven.org/private-properties-in-javascript )
fuente
La única forma de obtener una verdadera privacidad en JS es a través del alcance, por lo que no hay forma de tener una propiedad que sea miembro de la
this
que solo se pueda acceder dentro del componente. La mejor manera de almacenar datos verdaderamente privados en ES6 es con un WeakMap.Obviamente, esto es probablemente un proceso lento y definitivamente feo, pero proporciona privacidad.
Tenga en cuenta que AUN ESTO no es perfecto, porque Javascript es muy dinámico. Alguien todavía podría hacer
para capturar los valores a medida que se almacenan, por lo que si desea ser más cuidadoso, necesitaría capturar una referencia local
.set
y.get
usarla explícitamente en lugar de confiar en el prototipo reemplazable.fuente
get
a uno por método (por ejemploconst _ = privates.get(this); console.log(_.privateProp1);
).const myObj = new SomeClass(); console.log(privateProp1.get(myObj)) // "I am Private1"
que significa que su propiedad es privada o no?Para futuras referencias de otros usuarios, escucho ahora que la recomendación es usar WeakMaps para guardar datos privados.
Aquí hay un ejemplo más claro y funcional:
fuente
Depende de a quién le preguntes :-)
No
private
se incluye ningún modificador de propiedad en la propuesta de Clases mínimamente máximas que parece haber llegado al borrador actual .Sin embargo, puede haber soporte para nombres privados , lo que sí permite propiedades privadas, y probablemente también podrían usarse en definiciones de clase.
fuente
Usar módulos ES6 (inicialmente propuesto por @ d13) funciona bien para mí. No imita perfectamente las propiedades privadas, pero al menos puede estar seguro de que las propiedades que deberían ser privadas no se filtrarán fuera de su clase. Aquí hay un ejemplo:
algo.js
Entonces el código consumidor puede verse así:
Actualización (importante):
Como @DanyalAytekin describió en los comentarios, estas propiedades privadas son estáticas, por lo tanto, de alcance global. Funcionarán bien cuando trabajen con Singletons, pero se debe tener cuidado con los objetos transitorios. Ampliando el ejemplo anterior:
fuente
private static
.a.say(); // a
debería serb.say(); // b
let _message = null
manera probada , no tan genial, cuando llama al constructor varias veces, se equivoca.Completando @ d13 y los comentarios de @ johnny-oshika y @DanyalAytekin:
Supongo que en el ejemplo proporcionado por @ johnny-oshika podríamos usar funciones normales en lugar de funciones de flecha y luego
.bind
usarlas con el objeto actual más un_privates
objeto como parámetro curry:algo.js
main.js
Beneficios en los que puedo pensar:
_greet
y_updateMessage
actuar como métodos privados siempre que no tengamosexport
las referencias)_privates
objeto enlazadoAlgunos inconvenientes en los que puedo pensar:
Puede encontrar un fragmento en ejecución aquí: http://www.webpackbin.com/NJgI5J8lZ
fuente
Sí, puede crear propiedades encapsuladas , pero no se ha hecho con modificadores de acceso (públicos | privados) al menos no con ES6.
Aquí hay un ejemplo simple de cómo se puede hacer con ES6:
1 Crear clase usando class palabra de
2 En su interior, el constructor declara la variable de ámbito de bloque utilizando let OR const palabras reservadas -> dado que son de ámbito de bloque, no se puede acceder desde afuera (encapsulado)
3 Para permitir cierto control de acceso (setters | getters) a esas variables, puede declarar el método de instancia dentro de su constructor usando:
this.methodName=function(){}
sintaxisAhora vamos a comprobarlo:
fuente
new Something();
porque sus métodos se declaran en el constructor para tener acceso a estos variables privadas Eso puede causar un gran consumo de memoria si crea muchas instancias de su clase, por lo que hay problemas de rendimiento. Los métodos deberían haberse declarado fuera del alcance del constructor. Mi comentario fue más una explicación de los inconvenientes de su solución que una crítica.Un enfoque diferente a "privado"
En lugar de luchar contra el hecho de que la visibilidad privada no está disponible actualmente en ES6, decidí adoptar un enfoque más práctico que funcione bien si su IDE admite JSDoc (por ejemplo, Webstorm). La idea es usar la
@private
etiqueta . En cuanto al desarrollo, el IDE le impedirá acceder a cualquier miembro privado desde fuera de su clase. Funciona bastante bien para mí y ha sido realmente útil para ocultar métodos internos, por lo que la función de autocompletar me muestra exactamente lo que la clase realmente quería exponer. Aquí hay un ejemplo:fuente
@private
comentario no puede evitar esto, es solo una característica para la generación de documentación y su IDE.WeakMap
Object.getOwnPropertySymbols
)Primero, defina una función para envolver WeakMap:
Luego, construya una referencia fuera de su clase:
Nota: IE11 no admite la clase , pero se ve más limpia en el ejemplo.
fuente
¡Oh, tantas soluciones exóticas! Por lo general, no me importa la privacidad, así que uso "seudo privacidad" como se dice aquí . Pero si te importa (si hay algunos requisitos especiales para eso), uso algo como en este ejemplo:
Otra posible implementación de la función (constructor)
Job
:fuente
Personalmente, me gusta la propuesta del operador de
::
vinculación y luego la combinaría con la solución @ d13 mencionada, pero por ahora quédese con la respuesta de @ d13 donde usa laexport
palabra clave para su clase y coloca las funciones privadas en el módulo.Hay una solución más difícil que no se ha mencionado aquí que sigue es un enfoque más funcional y le permitiría tener todos los accesorios / métodos privados dentro de la clase.
Private.js
Test.js
Se agradecerán sus comentarios al respecto.
fuente
Encontré esta publicación cuando buscaba las mejores prácticas para "datos privados para clases". Se mencionó que algunos de los patrones tendrían problemas de rendimiento.
Reuní algunas pruebas jsperf basadas en los 4 patrones principales del libro en línea "Explorando ES6":
http://exploringjs.com/es6/ch_classes.html#sec_private-data-for-classes
Las pruebas se pueden encontrar aquí:
https://jsperf.com/private-data-for-classes
En Chrome 63.0.3239 / Mac OS X 10.11.6, los patrones de mejor desempeño fueron "Datos privados a través de entornos de constructor" y "Datos privados a través de una convención de nomenclatura". Para mí, Safari funcionó bien para WeakMap pero Chrome no tan bien.
No sé el impacto de la memoria, pero el patrón de "entornos de constructor" que algunos habían advertido que sería un problema de rendimiento fue muy eficaz.
Los 4 patrones básicos son:
Datos privados a través de entornos de constructor
Datos privados a través de entornos de constructor 2
Datos privados a través de una convención de nomenclatura
Datos privados a través de WeakMaps
Datos privados a través de símbolos
fuente
Creo que es posible obtener 'lo mejor de ambos mundos' usando cierres dentro de los constructores. Hay dos variaciones:
Todos los miembros de datos son privados.
Algunos miembros son privados.
NOTA: Esto es ciertamente feo. Si conoce una solución mejor, edite esta respuesta.
fuente
De hecho, es posible usar símbolos y proxies. Utiliza los símbolos en el alcance de la clase y establece dos trampas en un proxy: una para el prototipo de clase para que Reflect.ownKeys (instancia) u Object.getOwnPropertySymbols no revele sus símbolos, la otra es para el propio constructor así que cuando
new ClassName(attrs)
se llama, la instancia devuelta será interceptada y tendrá bloqueados los propios símbolos de propiedades. Aquí está el código:Reflect.ownKeys()
funciona así:Object.getOwnPropertyNames(myObj).concat(Object.getOwnPropertySymbols(myObj))
por eso necesitamos una trampa para estos objetos.fuente
Incluso Typecript no puede hacerlo. De su documentación :
Pero transpilado en su patio de recreo esto da:
Por lo tanto, su palabra clave "privada" no es efectiva.
fuente
Llegué muy tarde a esta fiesta, pero presioné la pregunta OP en una búsqueda, así que ... Sí, puede tener propiedades privadas envolviendo la declaración de clase en un cierre
Hay un ejemplo de cómo tengo métodos privados en este codepen . En el fragmento a continuación, la clase Suscribible tiene dos funciones 'privadas'
process
yprocessCallbacks
. Cualquier propiedad se puede agregar de esta manera y se mantienen privadas mediante el uso del cierre. La privacidad de la OMI es una necesidad poco frecuente si las preocupaciones están bien separadas y no es necesario que Javascript se hinche agregando más sintaxis cuando un cierre funciona perfectamente.Me gusta este enfoque porque separa bien las preocupaciones y mantiene las cosas realmente privadas. El único inconveniente es la necesidad de usar 'self' (o algo similar) para referirse a 'this' en el contenido privado.
fuente
Creo que la respuesta de Benjamin es probablemente la mejor para la mayoría de los casos hasta que el lenguaje admita de forma nativa variables explícitamente privadas.
Sin embargo, si por alguna razón necesita evitar el acceso
Object.getOwnPropertySymbols()
, un método que he considerado usar es adjuntar una propiedad única, no configurable, no enumerable y no grabable que se puede usar como un identificador de propiedad para cada objeto en la construcción (como una únicaSymbol
, si aún no tiene otra propiedad única como unaid
). Luego, simplemente mantenga un mapa de las variables 'privadas' de cada objeto usando ese identificador.La ventaja potencial de este enfoque sobre el uso de a
WeakMap
es un tiempo de acceso más rápido si el rendimiento se convierte en una preocupación.fuente
destroy()
método que debería ser llamado por el código de uso cada vez que un objeto necesita ser eliminado.Sí, totalmente puede, y muy fácilmente también. Esto se hace exponiendo sus variables y funciones privadas devolviendo el gráfico del objeto prototipo en el constructor. Esto no es nada nuevo, pero toma un poco de js foo para entender la elegancia del mismo. De esta manera, no se utiliza un ámbito global o mapas débiles. Es una forma de reflexión integrada en el lenguaje. Dependiendo de cómo aproveche esto; uno puede forzar una excepción que interrumpe la pila de llamadas o enterrar la excepción como un
undefined
. Esto se muestra a continuación, y puede leer más sobre estas características aquí.fuente
fuente
console.log(instance.property)
debe arrojar o darle indefinido, no devolverle "prueba".Otra forma similar a las dos últimas publicadas
fuente
La mayoría de las respuestas dicen que es imposible o requieren que use un WeakMap o Symbol, que son características de ES6 que probablemente requerirían polyfills. Sin embargo, hay otra manera! 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
Vea esta respuesta para obtener una solución limpia y simple de 'clase' con una interfaz pública y privada y soporte para composición
fuente
Encontré una solución muy simple, solo utilícela
Object.freeze()
. Por supuesto, el problema es que no puede agregar nada al objeto más tarde.fuente
setName(name) { this.name = name; }
Yo uso este patrón y siempre me ha funcionado
fuente
En realidad es posible.
1. Primero, cree la clase y en el constructor devuelva la
_public
función llamada .2. En la
_public
función llamada , pase lathis
referencia (para obtener acceso a todos los métodos y accesorios privados) y todos los argumentos deconstructor
(que se pasaránnew Names()
)3. En el
_public
alcance de la función también está laNames
clase con acceso athis
(_esto ) referencia de laNames
clase privadafuente
Puedes probar esto https://www.npmjs.com/package/private-members
Este paquete guardará los miembros por instancia.
fuente