tipos orientado objetos herencia ejemplos clases javascript private-members

orientado - prototype javascript ejemplos



Acceder a variables de miembros privados a partir de funciones definidas por prototipos (24)

¿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.


Actualización: con ES6, hay una mejor manera:

Para resumir, puedes usar el nuevo Symbol para crear campos privados.
Aquí hay una gran descripción: https://curiosity-driven.org/private-properties-in-javascript

Ejemplo:

var Person = (function() { // Only Person can access nameSymbol var nameSymbol = Symbol(''name''); function Person(name) { this[nameSymbol] = name; } Person.prototype.getName = function() { return this[nameSymbol]; }; return Person; }());

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 prototípica. 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 de prototipos 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 que es 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 nombrar convenciones para _private fields.

No te molestes mezclando 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, debes exponer el cierre a this , lo que significa que lo estás exponiendo públicamente de una manera 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.), cumpla con la convención de nomenclatura "privada" y no se moleste con los cierres.

No entiendo por qué los desarrolladores de JS intentan TAN difícil hacer campos realmente privados.


¡Intentalo!

function Potatoe(size) { var _image = new Image(); _image.src = ''potatoe_''+size+''.png''; function getImage() { if (getImage.caller == null || getImage.caller.owner != Potatoe.prototype) throw new Error(''This is a private property.''); return _image; } Object.defineProperty(this,''image'',{ configurable: false, enumerable: false, get : getImage }); Object.defineProperty(this,''size'',{ writable: false, configurable: false, enumerable: true, value : size }); } Potatoe.prototype.draw = function(ctx,x,y) { //ctx.drawImage(this.image,x,y); console.log(this.image); } Potatoe.prototype.draw.owner = Potatoe.prototype; var pot = new Potatoe(32); console.log(''Potatoe size: ''+pot.size); try { console.log(''Potatoe image: ''+pot.image); } catch(e) { console.log(''Oops: ''+e); } pot.draw();


@Kai

Eso no funcionará Si lo haces

var t2 = new TestClass();

entonces 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 morgancodes.

Hasta ahora, no he encontrado una forma fácil y limpia de hacerlo sin tener que introducir un hash privado y funciones de limpieza adicionales. Una función de miembro privado se puede simular en cierta medida:

(function() { function Foo() { ... } Foo.prototype.bar = function() { privateFoo.call(this, blah); }; function privateFoo(blah) { // scoped to the instance by passing this to call } window.Foo = Foo; }());


Aquí hay algo que he encontrado 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.

// pseudo-class definition scope (function () { // this is used to identify ''friend'' functions defined within this scope, // while not being able to forge valid parameter for GetContext() // to gain ''private'' access from outside var _scope = new (function () { })(); // ----------------------------------------------------------------- // pseudo-class definition this.Something = function (x) { // ''private'' members are wrapped into context object, // it can be also created with a function var _ctx = Object.seal({ // actual private members Name: null, Number: null, Somefunc: function () { console.log(''Something('' + this.Name + '').Somefunc(): number = '' + this.Number); } }); // ----------------------------------------------------------------- // function below needs to be defined in every class // to allow limited access from prototype this.GetContext = function (scope) { if (scope !== _scope) throw ''access''; return _ctx; } // ----------------------------------------------------------------- { // initialization code, if any _ctx.Name = (x !== ''undefined'') ? x : ''default''; _ctx.Number = 0; Object.freeze(this); } } // ----------------------------------------------------------------- // prototype is defined only once this.Something.prototype = Object.freeze({ // public accessors for ''private'' field get Number() { return this.GetContext(_scope).Number; }, set Number(v) { this.GetContext(_scope).Number = v; }, // public function making use of some private fields Test: function () { var _ctx = this.GetContext(_scope); // access ''private'' field console.log(''Something('' + _ctx.Name + '').Test(): '' + _ctx.Number); // call ''private'' func _ctx.Somefunc(); } }); // ----------------------------------------------------------------- // wrap is used to hide _scope value and group definitions }).call(this); function _A(cond) { if (cond !== true) throw new Error(''assert failed''); } // ----------------------------------------------------------------- function test_smth() { console.clear(); var smth1 = new Something(''first''), smth2 = new Something(''second''); //_A(false); _A(smth1.Test === smth2.Test); smth1.Number = 3; smth2.Number = 5; console.log(''smth1.Number: '' + smth1.Number + '', smth2.Number: '' + smth2.Number); smth1.Number = 2; smth2.Number = 6; smth1.Test(); smth2.Test(); try { var ctx = smth1.GetContext(); } catch (err) { console.log(''error: '' + err); } } test_smth();


Cuando lo leí, sonó 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 tuvieras 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.

var SharedPrivateClass = (function(){ // use immediate function // our private data var private = "Default"; // create the constructor function SharedPrivateClass () {} // add to the prototype SharedPrivateClass.prototype.getPrivate = function () { // It has access to private vars from the immediate function! return private; } SharedPrivateClass.prototype.setPrivate = function (value) { private = value; } return SharedPrivateClass; })(); var a = new SharedPrivateClass(); console.log("a:", a.getPrivate()); // "a: Default" var b = new SharedPrivateClass(); console.log("b:", b.getPrivate()); // "b: Default" a.setPrivate("foo"); // a Sets private to ''foo'' console.log("a:", a.getPrivate()); // "a: foo" console.log("b:", b.getPrivate()); // oh no, b.getPrivate() is ''foo''! console.log(a.hasOwnProperty("getPrivate")); // false. belongs to the prototype console.log(a.private); // undefined

Hay muchos casos en 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 estáticas privadas.

Si necesita acceder a las variables en un espacio de nombres privado desde sus métodos definidos en el prototipo, puede probar este patrón.

var PrivateNamespaceClass = (function(){ // immediate function var instance = 0, // counts the number of instances defaultName = "Default Name", p = []; // an array of private objects // careate the constructor function PrivateNamespaceClass () { // Increment the instance count and save it to the instance. // This will become your key to your private space. this.i = instance++; // Create a new object in the private space. p[this.i] = {}; // Define properties or methods in the private space. p[this.i].name = defaultName; console.log("New instance " + this.i); } PrivateNamespaceClass.prototype.getPrivateName = function () { // It has access to the private space and it''s children! return p[this.i].name; } PrivateNamespaceClass.prototype.setPrivateName = function (value) { // Because you use the instance number assigned to the object (this.i) // as a key, the values set will not change in other instances. p[this.i].name = value; return "Set " + p[this.i].name; } return PrivateNamespaceClass; })(); var a = new PrivateNamespaceClass(); console.log(a.getPrivateName()); // Default Name var b = new PrivateNamespaceClass(); console.log(b.getPrivateName()); // Default Name console.log(a.setPrivateName("A")); console.log(b.setPrivateName("B")); console.log(a.getPrivateName()); // A console.log(b.getPrivateName()); // B console.log(a.privateNamespace); // undefined

Me encantaría recibir comentarios de cualquiera que vea un error con esta forma de hacerlo.


De hecho, puedes lograr esto usando la verificación de accesorios :

(function(key, global) { // Creates a private data accessor function. function _(pData) { return function(aKey) { return aKey === key && pData; }; } // Private data accessor verifier. Verifies by making sure that the string // version of the function looks normal and that the toString function hasn''t // been modified. NOTE: Verification can be duped if the rogue code replaces // Function.prototype.toString before this closure executes. function $(me) { if(me._ + '''' == _asString && me._.toString === _toString) { return me._(key); } } var _asString = _({}) + '''', _toString = _.toString; // Creates a Person class. var PersonPrototype = (global.Person = function(firstName, lastName) { this._ = _({ firstName : firstName, lastName : lastName }); }).prototype; PersonPrototype.getName = function() { var pData = $(this); return pData.firstName + '' '' + pData.lastName; }; PersonPrototype.setFirstName = function(firstName) { var pData = $(this); pData.firstName = firstName; return this; }; PersonPrototype.setLastName = function(lastName) { var pData = $(this); pData.lastName = lastName; return this; }; })({}, this); var chris = new Person(''Chris'', ''West''); alert(chris.setFirstName(''Christopher'').setLastName(''Webber'').getName());

Este ejemplo proviene de mi publicación sobre Funciones prototípicas y datos privados y se explica con más detalle allí.


En el JavaScript actual, estoy bastante seguro de que hay una y solo una forma de tener un estado privado , accesible desde funciones de prototipo , sin agregar nada público a this . La respuesta es usar el patrón de "mapa débil".

Para resumir: la clase Person tiene un solo mapa débil, donde las claves son las instancias de Persona, y los valores son objetos simples que se utilizan para el almacenamiento privado.

Aquí hay un ejemplo completamente funcional: (juega en http://jsfiddle.net/ScottRippey/BLNVr/ )

var Person = (function() { var _ = weakMap(); // Now, _(this) returns an object, used for private storage. var Person = function(first, last) { // Assign private storage: _(this).firstName = first; _(this).lastName = last; } Person.prototype = { fullName: function() { // Retrieve private storage: return _(this).firstName + _(this).lastName; }, firstName: function() { return _(this).firstName; }, destroy: function() { // Free up the private storage: _(this, true); } }; return Person; })(); function weakMap() { var instances=[], values=[]; return function(instance, destroy) { var index = instances.indexOf(instance); if (destroy) { // Delete the private state: instances.splice(index, 1); return values.splice(index, 1)[0]; } else if (index === -1) { // Create the private state: instances.push(instance); values.push({}); return values[values.length - 1]; } else { // Return the private state: return values[index]; } }; }

Como dije, esta es realmente la única forma de lograr las 3 partes.

Sin embargo, hay dos advertencias. En primer lugar, esto cuesta rendimiento: cada vez que accede a los datos privados, se trata de una operación O(n) , donde n es el número de instancias. Entonces no querrás hacer esto si tienes una gran cantidad de instancias. En segundo lugar, cuando haya terminado con una instancia, debe llamar a destroy ; de lo contrario, la instancia y los datos no será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 ceñirme.


Esto es lo que se me ocurrió.

(function () { var staticVar = 0; var yrObj = function () { var private = {"a":1,"b":2}; var MyObj = function () { private.a += staticVar; staticVar++; }; MyObj.prototype = { "test" : function () { console.log(private.a); } }; return new MyObj; }; window.YrObj = yrObj; }()); var obj1 = new YrObj; var obj2 = new YrObj; obj1.test(); // 1 obj2.test(); // 2

el principal problema con esta implementación es que redefine los prototipos en cada instanciación.


Hay una manera más simple de aprovechar el uso de métodos de bind y call .

Al establecer variables privadas para un objeto, puede aprovechar el alcance de ese objeto.

Ejemplo

function TestClass (value) { // The private value(s) var _private = { value: value }; // `bind` creates a copy of `getValue` when the object is instantiated this.getValue = TestClass.prototype.getValue.bind(_private); // Use `call` in another function if the prototype method will possibly change this.getValueDynamic = function() { return TestClass.prototype.getValue.call(_private); }; }; TestClass.prototype.getValue = function() { return this.value; };

Este método no está exento de inconvenientes. Dado que el contexto del alcance se está anulando efectivamente, no tiene acceso fuera del objeto _private . Sin embargo, no es imposible, sin embargo, dar acceso al alcance del objeto de la instancia. Puede pasar el contexto del objeto ( this ) como el segundo argumento para bind o call para seguir teniendo acceso a sus valores públicos en la función de prototipo.

Accediendo a valores públicos

function TestClass (value) { var _private = { value: value }; this.message = "Hello, "; this.getMessage = TestClass.prototype.getMessage.bind(_private, this); } TestClass.prototype.getMessage = function(_public) { // Can still access passed in arguments // e.g. – test.getValues(''foo''), ''foo'' is the 2nd argument to the method console.log([].slice.call(arguments, 1)); return _public.message + this.value; }; var test = new TestClass("World"); test.getMessage(1, 2, 3); // [1, 2, 3] (console.log) // => "Hello, World" (return value) test.message = "Greetings, "; test.getMessage(); // [] (console.log) // => "Greetings, World" (return value)


Hay una manera muy simple de hacer esto

function SharedPrivate(){ var private = "secret"; this.constructor.prototype.getP = function(){return private} this.constructor.prototype.setP = function(v){ private = v;} } var o1 = new SharedPrivate(); var o2 = new SharedPrivate(); console.log(o1.getP()); // secret console.log(o2.getP()); // secret o1.setP("Pentax Full Frame K1 is on sale..!"); console.log(o1.getP()); // Pentax Full Frame K1 is on sale..! console.log(o2.getP()); // Pentax Full Frame K1 is on sale..! o2.setP("And it''s only for $1,795._"); console.log(o1.getP()); // And it''s only for $1,795._

Los prototipos de JavaScript son dorados.


Llego tarde a la fiesta, pero creo que puedo contribuir. Aquí, mira 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

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 llave .

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 el constructor o prototipo, pero no en ningún otro lado. No he visto este método usado en ninguna parte, pero creo que es realmente poderoso.


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 ámbito en el que fueron definidas.

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.

Aún puede tener variables privadas, pero si desea que los métodos definidos en el prototipo tengan acceso a ellas, debe definir getters y setters en this objeto, a los que tendrán acceso los métodos prototipo (junto con todo lo demás). Por ejemplo:

function Person(name, secret) { // public this.name = name; // private var secret = secret; // public methods have access to private members this.setSecret = function(s) { secret = s; } this.getSecret = function() { return secret; } } // Must use getters/setters Person.prototype.spillSecret = function() { alert(this.getSecret()); };


Si es posible. El patrón de diseño PPF simplemente resuelve esto.

PPF significa Funciones de Prototipo Privado. PPF básico resuelve estos problemas:

  1. Las funciones de prototipo obtienen acceso a datos de instancia privados.
  2. Las funciones del prototipo pueden hacerse privadas.

Para el primero, solo:

  1. Coloque todas las variables de instancia privadas a las que desee acceder desde las funciones de prototipo dentro de un contenedor de datos separado, y
  2. Pase una referencia al contenedor de datos a todas las funciones del prototipo como parámetro.

Es así de simple. Por ejemplo:

// Helper class to store private data. function Data() {}; // Object constructor function Point(x, y) { // container for private vars: all private vars go here // we want x, y be changeable via methods only var data = new Data; data.x = x; data.y = y; ... } // Prototype functions now have access to private instance data Point.prototype.getX = function(data) { return data.x; } Point.prototype.getY = function(data) { return data.y; }

...

Lee la historia completa aquí:

Patrón de diseño PPF


Sugiero que probablemente sería una buena idea describir "tener una asignación de prototipo en un constructor" como un anti patrón de Javascript. Piénsalo. Es demasiado arriesgado.

Lo que en realidad está haciendo allí al crear el segundo objeto (es decir, b) está redefiniendo esa función de 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 parece demasiado arriesgado.

Encontré un error en el Javascript en el que estaba trabajando recientemente debido a este anti-patrón exacto. Intentaba establecer un controlador de arrastrar y soltar en el objeto particular que se estaba creando, pero en cambio lo estaba haciendo para todas las instancias. No está bien.

La solución de Doug Crockford es la mejor.


vea la página de Doug Crockford sobre esto . Tienes que hacerlo indirectamente con algo que pueda acceder al alcance de la variable privada.

otro ejemplo:

Incrementer = function(init) { var counter = init || 0; // "counter" is a private variable this._increment = function() { return counter++; } this._set = function(x) { counter = x; } } Incrementer.prototype.increment = function() { return this._increment(); } Incrementer.prototype.set = function(x) { return this._set(x); }

caso de uso:

js>i = new Incrementer(100); [object Object] js>i.increment() 100 js>i.increment() 101 js>i.increment() 102 js>i.increment() 103 js>i.set(-44) js>i.increment() -44 js>i.increment() -43 js>i.increment() -42


Can''t you put the variables in a higher scope?

(function () { var privateVariable = true; var MyClass = function () { if (privateVariable) console.log(''readable from private scope!''); }; MyClass.prototype.publicMethod = function () { if (privateVariable) console.log(''readable from public scope!''); }; }))();


I faced the exact same question today and after elaborating on Scott Rippey first-class response, I came up with a very simple solution (IMHO) that is both compatible with ES5 and efficient, it also is name clash safe (using _private seems unsafe).

/*jslint white: true, plusplus: true */ /*global console */ var a, TestClass = (function(){ "use strict"; function PrefixedCounter (prefix) { var counter = 0; this.count = function () { return prefix + (++counter); }; } var TestClass = (function(){ var cls, pc = new PrefixedCounter("_TestClass_priv_") , privateField = pc.count() ; cls = function(){ this[privateField] = "hello"; this.nonProtoHello = function(){ console.log(this[privateField]); }; }; cls.prototype.prototypeHello = function(){ console.log(this[privateField]); }; return cls; }()); return TestClass; }()); a = new TestClass(); a.nonProtoHello(); a.prototypeHello();

Tested with ringojs and nodejs. I''m eager to read your opinion.


I have one solution, but I am not sure it is without flaws.

For it to work, you have to use the following structure:

  1. Use 1 private object that contains all private variables.
  2. Use 1 instance function.
  3. Apply a closure to the constructor and all prototype functions.
  4. Any instance created is done outside the closure defined.

Aquí está el código:

var TestClass = (function () { // difficult to be guessed. var hash = Math.round(Math.random() * Math.pow(10, 13) + + new Date()); var TestClass = function () { var privateFields = { field1: 1, field2: 2 }; this.getPrivateFields = function (hashed) { if(hashed !== hash) { throw "Cannot access private fields outside of object."; // or return null; } return privateFields; }; }; TestClass.prototype.prototypeHello = function () { var privateFields = this.getPrivateFields(hash); privateFields.field1 = Math.round(Math.random() * 100); privateFields.field2 = Math.round(Math.random() * 100); }; TestClass.prototype.logField1 = function () { var privateFields = this.getPrivateFields(hash); console.log(privateFields.field1); }; TestClass.prototype.logField2 = function () { var privateFields = this.getPrivateFields(hash); console.log(privateFields.field2); }; return TestClass; })();

How this works is that it provides an instance function "this.getPrivateFields" to access the "privateFields" private variables object, but this function will only return the "privateFields" object inside the main closure defined (also prototype functions using "this.getPrivateFields" need to be defined inside this closure).

A hash produced during runtime and difficult to be guessed is used as parameters to make sure that even if "getPrivateFields" is called outside the scope of closure will not return the "privateFields" object.

The drawback is that we can not extend TestClass with more prototype functions outside the closure.

Here is some test code:

var t1 = new TestClass(); console.log(''Initial t1 field1 is: ''); t1.logField1(); console.log(''Initial t1 field2 is: ''); t1.logField2(); t1.prototypeHello(); console.log(''t1 field1 is now: ''); t1.logField1(); console.log(''t1 field2 is now: ''); t1.logField2(); var t2 = new TestClass(); console.log(''Initial t2 field1 is: ''); t2.logField1(); console.log(''Initial t2 field2 is: ''); t2.logField2(); t2.prototypeHello(); console.log(''t2 field1 is now: ''); t2.logField1(); console.log(''t2 field2 is now: ''); t2.logField2(); console.log(''t1 field1 stays: ''); t1.logField1(); console.log(''t1 field2 stays: ''); t1.logField2(); t1.getPrivateFields(11233);

EDIT: Using this method, it is also possible to "define" private functions.

TestClass.prototype.privateFunction = function (hashed) { if(hashed !== hash) { throw "Cannot access private function."; } }; TestClass.prototype.prototypeHello = function () { this.privateFunction(hash); };


I''m just new to javascripting (I need to get acquainted with it) . I stumbled on the same question. For now I would go for a small lightweight wrapper function. This way each new instance will only reimplement the wrapper.

I think it should be something like this:

// your class function TestClass() { // your field this.privateField = "hello"; // create a local reference to this, // because ''this'' in the wrapper function will not be a TestClass var self = this; // implement the wrapper function and pass the ''self'' to the static version this.showHello = () => TestClass.showHello(self); } // implement the ''big/complex'' functionallity static. The wrapper will pass the instance TestClass.showHello = function(self) { alert(self.privateField); }

Uso:

var t = new TestClass(); t.showHello();

You could even call the static method:

TestClass.showHello(t);


Puede usar una asignación de prototipo dentro de la definición del constructor.

La variable será visible para el método de prototipo agregado, pero todas las instancias de las funciones tendrán acceso a la misma variable COMPARTIDA.

function A() { var sharedVar = 0; this.local = ""; A.prototype.increment = function(lval) { if (lval) this.local = lval; alert((++sharedVar) + " while this.p is still " + this.local); } } var a = new A(); var b = new A(); a.increment("I belong to a"); b.increment("I belong to b"); a.increment(); b.increment();

Espero que esto pueda ser útil.


También puede intentar agregar el método no directamente en el prototipo, sino en la función de constructor como esta:

var MyArray = function() { var array = []; this.add = MyArray.add.bind(null, array); this.getAll = MyArray.getAll.bind(null, array); } MyArray.add = function(array, item) { array.push(item); } MyArray.getAll = function(array) { return array; } var myArray1 = new MyArray(); myArray1.add("some item 1"); console.log(myArray1.getAll()); // [''some item 1''] var myArray2 = new MyArray(); myArray2.add("some item 2"); console.log(myArray2.getAll()); // [''some item 2''] console.log(myArray1.getAll()); // [''some item 2''] - FINE!


Was playing around with this today and this was the only solution I could find without using Symbols. Best thing about this is it can actually all be completely private.

The solution is based around a homegrown module loader which basically becomes the mediator for a private storage cache (using a weak map).

const loader = (function() { function ModuleLoader() {} //Static, accessible only if truly needed through obj.constructor.modules //Can also be made completely private by removing the ModuleLoader prefix. ModuleLoader.modulesLoaded = 0; ModuleLoader.modules = {} ModuleLoader.prototype.define = function(moduleName, dModule) { if (moduleName in ModuleLoader.modules) throw new Error(''Error, duplicate module''); const module = ModuleLoader.modules[moduleName] = {} module.context = { __moduleName: moduleName, exports: {} } //Weak map with instance as the key, when the created instance is garbage collected or goes out of scope this will be cleaned up. module._private = { private_sections: new WeakMap(), instances: [] }; function private(action, instance) { switch (action) { case "create": if (module._private.private_sections.has(instance)) throw new Error(''Cannot create private store twice on the same instance! check calls to create.'') module._private.instances.push(instance); module._private.private_sections.set(instance, {}); break; case "delete": const index = module._private.instances.indexOf(instance); if (index == -1) throw new Error(''Invalid state''); module._private.instances.slice(index, 1); return module._private.private_sections.delete(instance); break; case "get": return module._private.private_sections.get(instance); break; default: throw new Error(''Invalid action''); break; } } dModule.call(module.context, private); ModuleLoader.modulesLoaded++; } ModuleLoader.prototype.remove = function(moduleName) { if (!moduleName in (ModuleLoader.modules)) return; /* Clean up as best we can. */ const module = ModuleLoader.modules[moduleName]; module.context.__moduleName = null; module.context.exports = null; module.cotext = null; module._private.instances.forEach(function(instance) { module._private.private_sections.delete(instance) }); for (let i = 0; i < module._private.instances.length; i++) { module._private.instances[i] = undefined; } module._private.instances = undefined; module._private = null; delete ModuleLoader.modules[moduleName]; ModuleLoader.modulesLoaded -= 1; } ModuleLoader.prototype.require = function(moduleName) { if (!(moduleName in ModuleLoader.modules)) throw new Error(''Module does not exist''); return ModuleLoader.modules[moduleName].context.exports; } return new ModuleLoader(); })(); loader.define(''MyModule'', function(private_store) { function MyClass() { //Creates the private storage facility. Called once in constructor. private_store("create", this); //Retrieve the private storage object from the storage facility. private_store("get", this).no = 1; } MyClass.prototype.incrementPrivateVar = function() { private_store("get", this).no += 1; } MyClass.prototype.getPrivateVar = function() { return private_store("get", this).no; } this.exports = MyClass; }) //Get whatever is exported from MyModule const MyClass = loader.require(''MyModule''); //Create a new instance of `MyClass` const myClass = new MyClass(); //Create another instance of `MyClass` const myClass2 = new MyClass(); //print out current private vars console.log(''pVar = '' + myClass.getPrivateVar()) console.log(''pVar2 = '' + myClass2.getPrivateVar()) //Increment it myClass.incrementPrivateVar() //Print out to see if one affected the other or shared console.log(''pVar after increment = '' + myClass.getPrivateVar()) console.log(''pVar after increment on other class = '' + myClass2.getPrivateVar()) //Clean up. loader.remove(''MyModule'')


You need to change 3 things in your code:

  1. Replace var privateField = "hello" with this.privateField = "hello" .
  2. In the prototype replace privateField with this.privateField .
  3. In the non-prototype also replace privateField with this.privateField .

The final code would be the following:

TestClass = function(){ this.privateField = "hello"; this.nonProtoHello = function(){alert(this.privateField)}; } TestClass.prototype.prototypeHello = function(){alert(this.privateField)}; var t = new TestClass(); t.prototypeHello()


var getParams = function(_func) { res = _func.toString().split(''function ('')[1].split('')'')[0].split('','') return res } function TestClass(){ var private = {hidden: ''secret''} //clever magic accessor thing goes here if ( !(this instanceof arguments.callee) ) { for (var key in arguments) { if (typeof arguments[key] == ''function'') { var keys = getParams(arguments[key]) var params = [] for (var i = 0; i <= keys.length; i++) { if (private[keys[i]] != undefined) { params.push(private[keys[i]]) } } arguments[key].apply(null,params) } } } } TestClass.prototype.test = function(){ var _hidden; //variable I want to get TestClass(function(hidden) {_hidden = hidden}) //invoke magic to get }; new TestClass().test()

How''s this? Using an private accessor. Only allows you to get the variables though not to set them, depends on the use case.