una tipos llamar herencia example es6 ecmascript definicion declarar como clases clase javascript class ecmascript-6 private-members es2015

tipos - herencia de clases javascript



Propiedades privadas en clases de JavaScript ES6 (30)

Un enfoque diferente al "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 funciona bien si su IDE es compatible con JSDoc (por ejemplo, Webstorm). La idea es usar la etiqueta @private . En lo que respecta al desarrollo, el IDE le impedirá acceder a cualquier miembro privado 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:

¿Es posible crear propiedades privadas en clases 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"


Actualización: Una propuesta con una sintaxis más agradable está en camino. Las contribuciones son bienvenidas.

Sí, hay, para el acceso de ámbito en los objetos, ES6 introduce Symbol s .

Los símbolos son únicos, no puede obtener acceso a uno desde el exterior, excepto con la reflexión (como los datos privados en Java / C #), pero cualquiera que tenga acceso a un símbolo en el interior puede usarlo para el acceso clave:

var property = Symbol(); class Something { constructor(){ this[property] = "test"; } } var instance = new Something(); console.log(instance.property); //=> undefined, can only access with access to the Symbol


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 con el objeto actual más un objeto _privates como un parámetro al curry:

algo.js

function _greet(_privates) { return ''Hello '' + _privates.message; } function _updateMessage(_privates, newMessage) { _privates.message = newMessage; } export default class Something { constructor(message) { const _privates = { message }; this.say = _greet.bind(this, _privates); this.updateMessage = _updateMessage.bind(this, _privates); } }

main.js

import Something from ''./something.js''; const something = new Something(''Sunny day!''); const message1 = something.say(); something.updateMessage(''Cloudy day!''); const message2 = something.say(); console.log(message1 === ''Hello Sunny day!''); // true console.log(message2 === ''Hello Cloudy day!''); // true // the followings are not public console.log(something._greet === undefined); // true console.log(something._privates === undefined); // true console.log(something._updateMessage === undefined); // true // another instance which doesn''t share the _privates const something2 = new Something(''another Sunny day!''); const message3 = something2.say(); console.log(message3 === ''Hello another Sunny day!''); // true

Beneficios que se me ocurren:

  • podemos tener métodos privados ( _greet y _updateMessage actúan como métodos privados siempre y cuando no export las referencias)
  • aunque no están en el prototipo, los métodos mencionados anteriormente ahorrarán memoria porque las instancias se crean una vez, fuera de la clase (en lugar de definirlas en el constructor)
  • No perdemos ningún globo global ya que estamos dentro de un módulo.
  • También podemos tener propiedades privadas usando el objeto _privates

Algunos inconvenientes que se me ocurren:

Un fragmento de código en ejecución se puede encontrar aquí: http://www.webpackbin.com/NJgI5J8lZ


Creo que es posible obtener ''lo mejor de ambos mundos'' usando cierres dentro de los constructores. Hay dos variaciones:

Todos los datos de los miembros son privados.

function myFunc() { console.log(''Value of x: '' + this.x); this.myPrivateFunc(); } function myPrivateFunc() { console.log(''Enhanced value of x: '' + (this.x + 1)); } class Test { constructor() { let internal = { x : 2, }; internal.myPrivateFunc = myPrivateFunc.bind(internal); this.myFunc = myFunc.bind(internal); } };

Algunos miembros son privados

NOTA: Es cierto que esto es feo. Si conoce una solución mejor, edite esta respuesta.

function myFunc(priv, pub) { pub.y = 3; // The Test object now gets a member ''y'' with value 3. console.log(''Value of x: '' + priv.x); this.myPrivateFunc(); } function myPrivateFunc() { pub.z = 5; // The Test object now gets a member ''z'' with value 3. console.log(''Enhanced value of x: '' + (priv.x + 1)); } class Test { constructor() { let self = this; let internal = { x : 2, }; internal.myPrivateFunc = myPrivateFunc.bind(null, internal, self); this.myFunc = myFunc.bind(null, internal, self); } };


De hecho, es posible utilizar símbolos y proxies. Utiliza los símbolos en el alcance de la clase y configura dos capturas en un proxy: una para el prototipo de la clase para que Reflect.ownKeys (instancia) o Object.getOwnPropertySymbols no regale sus símbolos, la otra es para el propio constructor por lo tanto, cuando se llama a un new ClassName(attrs) , la instancia devuelta será interceptada y tendrá bloqueados los propios símbolos de propiedades. Aquí está el código:

const Human = (function() { const pet = Symbol(); const greet = Symbol(); const Human = privatizeSymbolsInFn(function(name) { this.name = name; // public this[pet] = ''dog''; // private }); Human.prototype = privatizeSymbolsInObj({ [greet]() { // private return ''Hi there!''; }, revealSecrets() { console.log(this[greet]() + ` The pet is a ${this[pet]}`); } }); return Human; })(); const bob = new Human(''Bob''); console.assert(bob instanceof Human); console.assert(Reflect.ownKeys(bob).length === 1) // only [''name''] console.assert(Reflect.ownKeys(Human.prototype).length === 1 ) // only [''revealSecrets''] // Setting up the traps inside proxies: function privatizeSymbolsInObj(target) { return new Proxy(target, { ownKeys: Object.getOwnPropertyNames }); } function privatizeSymbolsInFn(Class) { function construct(TargetClass, argsList) { const instance = new TargetClass(...argsList); return privatizeSymbolsInObj(instance); } return new Proxy(Class, { construct }); }

Reflect.ownKeys() funciona así: Object.getOwnPropertyNames(myObj).concat(Object.getOwnPropertySymbols(myObj)) por eso necesitamos una trampa para estos objetos.



La única forma de obtener verdadera privacidad en JS es a través del alcance, por lo que no hay manera de tener una propiedad que sea miembro de this que solo sea accesible dentro del componente. La mejor manera de almacenar datos verdaderamente privados en ES6 es con un WeakMap.

const privateProp1 = new WeakMap(); const privateProp2 = new WeakMap(); class SomeClass { constructor() { privateProp1.set(this, "I am Private1"); privateProp2.set(this, "I am Private2"); this.publicVar = "I am public"; this.publicMethod = () => { console.log(privateProp1.get(this), privateProp2.get(this)) }; } printPrivate() { console.log(privateProp1.get(this)); } }

Obviamente, esto es probablemente lento, y definitivamente feo, pero proporciona privacidad.

Tenga en cuenta que AUN ESO no es perfecto, porque Javascript es muy dinámico. Alguien aun podría hacer

var oldSet = WeakMap.prototype.set; WeakMap.prototype.set = function(key, value){ // Store ''this'', ''key'', and ''value'' return oldSet.call(this, key, value); };

para capturar los valores a medida que se almacenan, por lo tanto, si quisiera tener un cuidado especial, tendría que capturar una referencia local a .set y .get para usar explícitamente en lugar de confiar en el prototipo reemplazable.

const {set: WMSet, get: WMGet} = WeakMap.prototype; const privateProp1 = new WeakMap(); const privateProp2 = new WeakMap(); class SomeClass { constructor() { WMSet.call(privateProp1, this, "I am Private1"); WMSet.call(privateProp2, this, "I am Private2"); this.publicVar = "I am public"; this.publicMethod = () => { console.log(WMGet.call(privateProp1, this), WMGet.call(privateProp2, this)) }; } printPrivate() { console.log(WMGet.call(privateProp1, this)); } }


La respuesta es no". Pero puedes crear acceso privado a propiedades como esta:

(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 el caso: https://mail.mozilla.org/pipermail/es-discuss/2014-January/035604.html y https://.com/a/22280202/1282216 . Para una discusión más larga sobre los símbolos y la privacidad, consulte: https://curiosity-driven.org/private-properties-in-javascript )


Los campos privados se están implementando en el estándar ECMA . Puedes comenzar a usarlos hoy con el preajuste de babel 7 y etapa 3. Ver el ejemplo de babel REPL .

class Something { #property; constructor(){ this.#property = "test"; } } const instance = new Something(); console.log(instance.property); //=> undefined


Para expandir la respuesta de @ loganfsmyth:

Los únicos datos verdaderamente privados en JavaScript siguen siendo variables de ámbito. No puede tener propiedades privadas en el sentido de las propiedades a las que se accede internamente de la misma forma que las propiedades públicas, pero puede usar variables de ámbito para almacenar datos privados.

Variables con 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 por rendimiento y memoria, pero algunos creen que la penalización es aceptable. La penalización se puede evitar para los métodos que no necesitan acceso a datos privados agregándolos al prototipo como de costumbre.

Ejemplo:

function Person(name) { let age = 20; // this is private this.name = name; // this is public this.greet = function () { // here we can access both name and age console.log(`name: ${this.name}, age: ${age}`); }; } let joe = new Person(''Joe''); joe.greet(); // here we can access name but not age

Scoped WeakMap

Se puede usar un WeakMap para evitar el rendimiento del enfoque anterior y la penalización de 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 alcance para crear un WeakMap privado, luego utilizamos 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 es necesario que vuelva a crear métodos solo para que accedan a sus propios WeakMaps.

Ejemplo:

let Person = (function () { let privateProps = new WeakMap(); class Person { constructor(name) { this.name = name; // this is public privateProps.set(this, {age: 20}); // this is private } greet() { // Here we can access both name and age console.log(`name: ${this.name}, age: ${privateProps.get(this).age}`); } } return Person; })(); let joe = new Person(''Joe''); joe.greet(); // here we can access joe''s name but not age

Este ejemplo usa un Objeto para usar un WeakMap para múltiples propiedades privadas; también puede usar múltiples WeakMaps y usarlos como age.set(this, 20) , o escribir un pequeño contenedor y usarlo de otra manera, como privateProps.set(this, ''age'', 0) .

La privacidad de este enfoque podría, en teoría, ser violada al manipular el objeto WeakMap global. Dicho esto, todo el JavaScript puede ser roto por globales destrozados. Nuestro código ya se basa en el supuesto de que esto no está sucediendo.

(Este método también se podría hacer con Map , pero WeakMap es mejor porque Map 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 de ámbito

Un símbolo es un tipo de valor primitivo que puede servir como un nombre de propiedad. Puede usar el método de variable con ámbito para crear un Símbolo privado, luego almacenar datos privados en this[mySymbol] .

La privacidad de este método puede Object.getOwnPropertySymbols utilizando Object.getOwnPropertySymbols , pero es un poco difícil de hacer.

Ejemplo:

let Person = (function () { let ageKey = Symbol(); class Person { constructor(name) { this.name = name; // this is public this[ageKey] = 20; // this is intended to be private } greet() { // Here we can access both name and age console.log(`name: ${this.name}, age: ${this[ageKey]}`); } } return Person; })(); let joe = new Person(''Joe''); joe.greet(); // Here we can access joe''s name and, with a little effort, age. ageKey is // not in scope, but we can obtain it by listing all Symbol properties on // joe with `Object.getOwnPropertySymbols(joe)`.

Media respuesta: guiones bajos

El antiguo predeterminado, solo usa una propiedad pública con un prefijo de subrayado. Aunque no es una propiedad privada de ninguna manera, esta convención prevalece tanto que hace un buen trabajo comunicando 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:

class Person { constructor(name) { this.name = name; // this is public this._age = 20; // this is intended to be private } greet() { // Here we can access both name and age console.log(`name: ${this.name}, age: ${this._age}`); } } let joe = new Person(''Joe''); joe.greet(); // Here we can access both joe''s name and age. But we know we aren''t // supposed to access his age, which just might stop us.

Conclusión

A partir de ES2017, todavía no hay una forma perfecta de hacer propiedades privadas. Varios enfoques tienen pros y contras. Las variables con alcance son verdaderamente privadas; Los WeakMaps con ámbito son muy privados y más prácticos que las variables con alcance; Los símbolos de alcance son razonablemente privados y razonablemente prácticos; Los guiones bajos son a menudo bastante privados y muy prácticos.


Para futuras referencias de otros usuarios, estoy escuchando ahora que la recomendación es usar WeakMaps para guardar datos privados.

Aquí hay un ejemplo más claro y funcional:

function storePrivateProperties(a, b, c, d) { let privateData = new WeakMap; // unique object as key, weak map can only accept object as key, when key is no longer referened, garbage collector claims the key-value let keyA = {}, keyB = {}, keyC = {}, keyD = {}; privateData.set(keyA, a); privateData.set(keyB, b); privateData.set(keyC, c); privateData.set(keyD, d); return { logPrivateKey(key) { switch(key) { case "a": console.log(privateData.get(keyA)); break; case "b": console.log(privateData.get(keyB)); break; case "c": console.log(privateData.get(keyC)); break; case "d": console.log(privateData.set(keyD)); break; default: console.log(`There is no value for ${key}`) } } } }


Personalmente, me gusta la propuesta del operador de vinculación :: y luego la combinaría con la solución @ d13 mencionada, pero por ahora se queda con la respuesta de @d13 donde utiliza la palabra clave de export 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 apoyos / métodos privados dentro de la clase.

Privado.js

export const get = state => key => state[key]; export const set = state => (key,value) => { state[key] = value; }

Prueba.js

import { get, set } from ''./utils/Private'' export default class Test { constructor(initialState = {}) { const _set = this.set = set(initialState); const _get = this.get = get(initialState); this.set(''privateMethod'', () => _get(''propValue'')); } showProp() { return this.get(''privateMethod'')(); } } let one = new Test({ propValue: 5}); let two = new Test({ propValue: 8}); two.showProp(); // 8 one.showProp(); // 5

Se agradecerían los comentarios al respecto.


Respuesta corta, no, no hay soporte nativo para propiedades privadas con clases ES6.

Pero puede imitar ese comportamiento al no adjuntar las nuevas propiedades al objeto, sino mantenerlas dentro de un constructor de clase, y usar captadores y definidores para alcanzar las propiedades ocultas. Tenga en cuenta que los captadores y definidores obtienen una nueva definición en cada nueva instancia de la clase.

ES6

class Person { constructor(name) { var _name = name this.setName = function(name) { _name = name; } this.getName = function() { return _name; } } }

ES5

function Person(name) { var _name = name this.setName = function(name) { _name = name; } this.getName = function() { return _name; } }


Usar módulos ES6 (inicialmente propuesto por @ d13) funciona bien para mí. No imita las propiedades privadas a la perfección, 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

let _message = null; const _greet = name => { console.log(''Hello '' + name); }; export default class Something { constructor(message) { _message = message; } say() { console.log(_message); _greet(''Bob''); } };

Entonces el código consumidor puede verse así:

import Something from ''./something.js''; const something = new Something(''Sunny day!''); something.say(); something._message; // undefined something._greet(); // exception

Actualización (Importante):

Como @DanyalAytekin se describe en los comentarios, estas propiedades privadas son estáticas, por lo tanto, de alcance global. Trabajarán bien cuando trabajen con Singletons, pero se debe tener cuidado con los objetos Transitorios. Extendiendo el ejemplo anterior:

import Something from ''./something.js''; import Something2 from ''./something.js''; const a = new Something(''a''); a.say(); // a const b = new Something(''b''); b.say(); // b const c = new Something2(''c''); c.say(); // c a.say(); // c b.say(); // c c.say(); // c


Sí, puede crear una propiedad encapsulada , pero no se ha hecho con modificadores de acceso (público | privado) 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 class

2 En su interior, el constructor declara la variable de ámbito de bloque utilizando las palabras reservadas const -> como son de ámbito de bloque, no se puede acceder a ellas desde el exterior (encapsulado)

3 Para permitir cierto control de acceso (definidores | getters) a esas variables, puede declarar el método de instancia dentro de su constructor usando: la this.methodName=function(){}

"use strict"; class Something{ constructor(){ //private property let property="test"; //private final (immutable) property const property2="test2"; //public getter this.getProperty2=function(){ return property2; } //public getter this.getProperty=function(){ return property; } //public setter this.setProperty=function(prop){ property=prop; } } }

Ahora vamos a comprobarlo:

var s=new Something(); console.log(typeof s.property);//undefined s.setProperty("another");//set to encapsulated `property` console.log(s.getProperty());//get encapsulated `property` value console.log(s.getProperty2());//get encapsulated immutable `property2` value


WeakMap

  • soportado en IE11 (los símbolos no lo son)
  • hard-private (los accesorios que usan Symbols son soft-private debido a Object.getOwnPropertySymbols )
  • puede verse realmente limpio (a diferencia de los cierres que requieren todos los accesorios y métodos en el constructor)

Primero, define una función para envolver WeakMap:

function Private() { const map = new WeakMap(); return obj => { let props = map.get(obj); if (!props) { props = {}; map.set(obj, props); } return props; }; }

Luego, construye una referencia fuera de tu clase:

const p = new Private(); class Person { constructor(name, age) { this.name = name; p(this).age = age; // it''s easy to set a private variable } getAge() { return p(this).age; // and get a private variable } }

Nota: la clase no es compatible con IE11, pero se ve más limpia en el ejemplo.


Otra forma similar a las dos últimas publicadas.

class Example { constructor(foo) { // privates const self = this; this.foo = foo; // public interface return self.public; } public = { // empty data nodata: { data: [] }, // noop noop: () => {}, } // everything else private bar = 10 } const test = new Example(''FOO''); console.log(test.foo); // undefined console.log(test.noop); // { data: [] } console.log(test.bar); // undefined


Utilizo este patrón y siempre me ha funcionado.

class Test { constructor(data) { class Public { constructor(prv) { // public function (must be in constructor on order to access "prv" variable) connectToDb(ip) { prv._db(ip, prv._err); } } // public function w/o access to "prv" variable log() { console.log("I''m logging"); } } // private variables this._data = data; this._err = function(ip) { console.log("could not connect to "+ip); } } // private function _db(ip, err) { if(!!ip) { console.log("connected to "+ip+", sending data ''"+this.data+"''"); return true; } else err(ip); } } var test = new Test(10), ip = "185.167.210.49"; test.connectToDb(ip); // true test.log(); // I''m logging test._err(ip); // undefined test._db(ip, function() { console.log("You have got hacked!"); }); // undefined


Aquí, la variable myThing es privada y es parte del cierre:

class Person { constructor() { var myThing = "Hello World"; return { thing: myThing, sayThing: this.sayThing } } sayThing() { console.log(this.thing); } } var person = new Person(); console.log(person);


Creo que la respuesta de Benjamin es probablemente la mejor para la mayoría de los casos hasta que el lenguaje respalde de forma nativa las variables 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, que se puede usar como un identificador de propiedad para cada objeto en construcción. (como un único Symbol, si aún no tiene alguna otra propiedad única como una id). Luego simplemente mantenga un mapa de las variables "privadas" de cada objeto usando ese identificador.

const privateVars = {}; class Something { constructor(){ Object.defineProperty(this, ''_sym'', { configurable: false, enumerable: false, writable: false, value: Symbol() }); var myPrivateVars = { privateProperty: "I''m hidden" }; privateVars[this._sym] = myPrivateVars; this.property = "I''m public"; } getPrivateProperty() { return privateVars[this._sym].privateProperty; } // A clean up method of some kind is necessary since the // variables won''t be cleaned up from memory automatically // when the object is garbage collected destroy() { delete privateVars[this._sym]; } } var instance = new Something(); console.log(instance.property); //=> "I''m public" console.log(instance.privateProperty); //=> undefined console.log(instance.getPrivateProperty()); //=> "I''m hidden"

La ventaja potencial de este enfoque sobre el uso de un tiempo de acceso más rápidoWeakMap es si el rendimiento se convierte en una preocupación.


En realidad es posible.
1. Primero, cree la clase y en el constructor devuelva la _publicfunción llamada .
2. En la _publicfunción llamada , pase la thisreferencia (para obtener acceso a todos los métodos y accesorios privados) y todos los argumentos de constructor (que se pasarán new Names())
3. En el _publicámbito de la función también está la Namesclase con el acceso a this(_this ) referencia de la Namesclase privada

class Names { constructor() { this.privateProperty = ''John''; return _public(this, arguments); } privateMethod() { } } const names = new Names(1,2,3); console.log(names.somePublicMethod); //[Function] console.log(names.publicProperty); //''Jasmine'' console.log(names.privateMethod); //undefined console.log(names.privateProperty); //undefind function _public(_this, _arguments) { class Names { constructor() { this.publicProperty = ''Jasmine''; _this.privateProperty; //"John"; _this.privateMethod; //[Function] } somePublicMethod() { _this.privateProperty; //"John"; _this.privateMethod; //[Function] } } return new Names(..._arguments); }


Encontré una solución muy simple, solo use Object.freeze(). Por supuesto, el problema es que no se puede agregar nada al objeto más adelante.

class Cat { constructor(name ,age) { this.name = name this.age = age Object.freeze(this) } } let cat = new Cat(''Garfield'', 5) cat.age = 6 // doesn''t work, even throws an error in strict mode


Incluso Typescript no puede hacerlo. De su documentation :

Cuando un miembro se marca como privado, no se puede acceder a él desde fuera de su clase contenedora. Por ejemplo:

class Animal { private name: string; constructor(theName: string) { this.name = theName; } } new Animal("Cat").name; // Error: ''name'' is private;

Pero transpilado en su documentation esto da:

var Animal = (function () { function Animal(theName) { this.name = theName; } return Animal; }()); console.log(new Animal("Cat").name);

Así que su palabra clave "privada" es ineficaz.


La mayoría de las respuestas dicen que es imposible, o requieren que uses un WeakMap o un Símbolo, que son características de ES6 que probablemente requieran polietileno. ¡Sin embargo hay otra manera! Echa un vistazo a esto:

// 1. Create closure var SomeClass = function() { // 2. Create `key` inside a closure var key = {}; // Function to create private storage var private = function() { var obj = {}; // return Function to access private storage using `key` return function(testkey) { if(key === testkey) return obj; // If `key` is wrong, then storage cannot be accessed console.error(''Cannot access private properties''); return undefined; }; }; var SomeClass = function() { // 3. Create private storage this._ = private(); // 4. Access private storage using the `key` this._(key).priv_prop = 200; }; SomeClass.prototype.test = function() { console.log(this._(key).priv_prop); // Using property from prototype }; return SomeClass; }(); // Can access private property from within prototype var instance = new SomeClass(); instance.test(); // `200` logged // Cannot access private property from outside of the closure var wrong_key = {}; instance._(wrong_key); // undefined; error logged

A este método lo llamo 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 . Usando 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 ninguna parte, pero creo que es realmente poderoso.


Llegando muy tarde a esta fiesta, pero llegué a la pregunta OP en una búsqueda, así que ... Sí, puedes tener propiedades privadas envolviendo la declaración de clase en un cierre

Hay un ejemplo de cómo tengo métodos privados en este código de código . En el fragmento a continuación, la clase Suscribible tiene dos funciones "privadas" processy processCallbacks. Cualquier propiedad se puede agregar de esta manera y se mantiene privada mediante el uso del cierre. La privacidad de la OMI es una necesidad rara si las preocupaciones están bien separadas y Javascript no necesita hincharse agregando más sintaxis cuando un cierre hace el trabajo.

const Subscribable = (function(){ const process = (self, eventName, args) => { self.processing.set(eventName, setTimeout(() => processCallbacks(self, eventName, args)))}; const processCallbacks = (self, eventName, args) => { if (self.callingBack.get(eventName).length > 0){ const [nextCallback, ...callingBack] = self.callingBack.get(eventName); self.callingBack.set(eventName, callingBack); process(self, eventName, args); nextCallback(...args)} else { delete self.processing.delete(eventName)}}; return class { constructor(){ this.callingBack = new Map(); this.processing = new Map(); this.toCallbacks = new Map()} subscribe(eventName, callback){ const callbacks = this.unsubscribe(eventName, callback); this.toCallbacks.set(eventName, [...callbacks, callback]); return () => this.unsubscribe(eventName, callback)} // callable to unsubscribe for convenience unsubscribe(eventName, callback){ let callbacks = this.toCallbacks.get(eventName) || []; callbacks = callbacks.filter(subscribedCallback => subscribedCallback !== callback); if (callbacks.length > 0) { this.toCallbacks.set(eventName, callbacks)} else { this.toCallbacks.delete(eventName)} return callbacks} emit(eventName, ...args){ this.callingBack.set(eventName, this.toCallbacks.get(eventName) || []); if (!this.processing.has(eventName)){ process(this, eventName, args)}}}})();

Me gusta este enfoque porque separa bien las preocupaciones y mantiene las cosas verdaderamente privadas. El único inconveniente es la necesidad de usar ''self'' (o algo similar) para referirse a ''this'' en el contenido privado.


Me encontré con 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 mejores patrones de rendimiento fueron "Datos privados a través de entornos de construcción" y "Datos privados a través de una convención de nombres". Para mí, Safari funcionó bien para WeakMap pero Chrome no tan bien.

No sé el impacto de la memoria, pero el patrón para "entornos de constructor" que algunos habían advertido sería un problema de rendimiento fue muy eficaz.

Los 4 patrones básicos son:

Datos privados a través de entornos de construcción.

class Countdown { constructor(counter, action) { Object.assign(this, { dec() { if (counter < 1) return; counter--; if (counter === 0) { action(); } } }); } } const c = new Countdown(2, () => {}); c.dec(); c.dec();

Datos privados a través de entornos constructores 2

class Countdown { constructor(counter, action) { this.dec = function dec() { if (counter < 1) return; counter--; if (counter === 0) { action(); } } } } const c = new Countdown(2, () => {}); c.dec(); c.dec();

Datos privados a través de una convención de nomenclatura.

class Countdown { constructor(counter, action) { this._counter = counter; this._action = action; } dec() { if (this._counter < 1) return; this._counter--; if (this._counter === 0) { this._action(); } } } const c = new Countdown(2, () => {}); c.dec(); c.dec();

Datos privados a través de WeakMaps

const _counter = new WeakMap(); const _action = new WeakMap(); class Countdown { constructor(counter, action) { _counter.set(this, counter); _action.set(this, action); } dec() { let counter = _counter.get(this); if (counter < 1) return; counter--; _counter.set(this, counter); if (counter === 0) { _action.get(this)(); } } } const c = new Countdown(2, () => {}); c.dec(); c.dec();

Datos privados a través de símbolos.

const _counter = Symbol(''counter''); const _action = Symbol(''action''); class Countdown { constructor(counter, action) { this[_counter] = counter; this[_action] = action; } dec() { if (this[_counter] < 1) return; this[_counter]--; if (this[_counter] === 0) { this[_action](); } } } const c = new Countdown(2, () => {}); c.dec(); c.dec();


Puede probar esto https://www.npmjs.com/package/private-members

Este paquete salvará a los miembros por instancia.

const pvt = require(''private-members''); const _ = pvt(); let Exemplo = (function () { function Exemplo() { _(this).msg = "Minha Mensagem"; } _().mensagem = function() { return _(this).msg; } Exemplo.prototype.showMsg = function () { let msg = _(this).mensagem(); console.log(msg); }; return Exemplo; })(); module.exports = Exemplo;


Sí totalmente puede, y bastante 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 tómese un poco de js foo para comprender su elegancia. De esta manera no se utiliza el ámbito global, o los mapas débiles. Es una forma de reflexión incorporada al lenguaje. Dependiendo de cómo se aproveche esto; uno puede forzar una excepción que interrumpa 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 here

class Clazz { constructor() { var _level = 1 function _private(x) { return _level * x; } return { level: _level, public: this.private, public2: function(x) { return _private(x); }, public3: function(x) { return _private(x) * this.public(x); }, }; } private(x) { return x * x; } } var clazz = new Clazz(); console.log(clazz._level); //undefined console.log(clazz._private); // undefined console.log(clazz.level); // 1 console.log(clazz.public(1)); //2 console.log(clazz.public2(2)); //2 console.log(clazz.public3(3)); //27 console.log(clazz.private(0)); //error


Vea esta respuesta para una solución de "clase" limpia y simple con una interfaz privada y pública y soporte para composición


class Something { constructor(){ var _property = "test"; Object.defineProperty(this, "property", { get: function(){ return _property} }); } } var instance = new Something(); console.log(instance.property); //=> "test" instance.property = "can read from outside, but can''t write"; console.log(instance.property); //=> "test"