2009-01-27 20 views
17

Bueno, traté de averiguar si esto es posible de alguna manera. Aquí está el código:miembro privado de Javascript en el prototipo

a=function(text) 
{ 
    var b=text; 
    if (!arguments.callee.prototype.get) 
     arguments.callee.prototype.get=function() 
    { 
     return b; 
    } 
    else 
     alert('already created!'); 
} 

var c=new a("test"); // creates prototype instance of getter 
var d=new a("ojoj"); // alerts already created 
alert(c.get())  // alerts test 
alert(d.get())  // alerts test from context of creating prototype function :(

Como ve, traté de crear un prototipo getter. ¿Para qué? Bueno, si usted escribe algo como esto:

a=function(text) 
{ 
    var b=text; 
    this.getText=function(){ return b} 
} 

... todo debería estar bien .. pero en realidad cada vez que crear el objeto - i crear la función getText que utiliza memoria. Me gustaría tener una función prototípica en la memoria que haría lo mismo ... ¿Alguna idea?

EDIT:

traté solución dada por Christoph, y parece que su única solución conocida por el momento. Es necesario recordar la información de identificación para recuperar el valor del contexto, pero toda la idea es agradable para mí :) Id es solo una cosa para recordar, todo lo demás se puede almacenar una vez en la memoria. De hecho, podría almacenar una gran cantidad de miembros privados de esta manera, y usar en cualquier momento solo una identificación. En realidad, esto me satisface :) (a menos que alguien tenga una mejor idea).

someFunc = function() 
{ 
    var store = new Array(); 
    var guid=0; 
    var someFunc = function(text) 
    { 
    this.__guid=guid; 
    store[guid++]=text; 
    } 

    someFunc.prototype.getValue=function() 
    { 
    return store[this.__guid]; 
    } 

    return someFunc; 
}() 

a=new someFunc("test"); 
b=new someFunc("test2"); 

alert(a.getValue()); 
alert(b.getValue()); 
+0

esto parece un duplicado de [otra pregunta] (http://stackoverflow.com/questions/436120) –

Respuesta

28

JavaScript tradicionalmente no proporcionaba un mecanismo para ocultar propiedades ('miembros privados').

Como JavaScript tiene un alcance léxico, siempre puede simular esto a nivel de objeto utilizando la función de constructor como cierre de sus 'miembros privados' y definiendo sus métodos en el constructor, pero esto no funcionará. métodos definidos en la propiedad prototipo del constructor.

Por supuesto, hay formas de evitar esto, pero yo no lo recomendaría:

Foo = (function() { 
    var store = {}, guid = 0; 

    function Foo() { 
     this.__guid = ++guid; 
     store[guid] = { bar : 'baz' }; 
    } 

    Foo.prototype.getBar = function() { 
     var privates = store[this.__guid]; 
     return privates.bar; 
    }; 

    Foo.prototype.destroy = function() { 
     delete store[this.__guid]; 
    }; 

    return Foo; 
})(); 

Esto almacenará las propiedades privadas '' en otra separada de su objeto Foo ejemplo. Asegúrese de llamar al destroy() después de que haya terminado con el objeto: de lo contrario, acaba de crear una fuga de memoria.


editar 12/01/2015: ECMAScript6 hace variantes mejoradas que no requieren la destrucción manual objeto posible, por ejemplo utilizando un WeakMap o preferiblemente un Symbol, evitando la necesidad de un almacén externo completo:

var Foo = (function() { 
    var bar = Symbol('bar'); 

    function Foo() { 
     this[bar] = 'baz'; 
    } 

    Foo.prototype.getBar = function() { 
     return this[bar]; 
    }; 

    return Foo; 
})(); 
+0

¿No significa eso que creo un objeto de función completamente nuevo cada vez que llamo a un nuevo Foo() ?? – Wilq32

+0

Algo no se ve aquí ... ¿cómo funciona esto cuando hay 2 instancias de la clase Foo? –

+0

oh, ya veo ... la "tienda" es compartida por toda la clase, pero la entrada en la tienda es por instancia. –

2

Los métodos del prototipo no pueden acceder a los miembros "privados", ya que existen en javascript; necesitas algún tipo de acceso privilegiado. Dado que declara get donde puede ver léxicamente b, siempre devolverá lo que b fue al crearlo.

2

Después de haberme inspirado enormemente en el trabajo conjunto de Christoph, se me ocurrió un concepto ligeramente modificado que proporciona algunas mejoras. Nuevamente, esta solución es interesante, pero no necesariamente recomendada.Estas mejoras incluyen:

  • Ya no es necesario realizar ninguna configuración en el constructor
  • quitado la necesidad de almacenar un GUID pública en casos
  • añadido un poco de azúcar sintáctica

En esencia, el truco aquí es usar el objeto de instancia en sí mismo como la clave para acceder al objeto privado asociado. Normalmente esto no es posible con objetos simples ya que sus claves deben ser cadenas. Sin embargo, pude lograr esto utilizando el hecho de que la expresión ({} === {}) devuelve false. En otras palabras, el operador de comparación puede discernir entre instancias de objetos únicos.

Para resumir, podemos utilizar dos matrices para mantener instancias y sus asociados objetos privados:

Foo = (function() { 
    var instances = [], privates = []; 

    // private object accessor function 
    function _(instance) { 
     var index = instances.indexOf(instance), privateObj; 

     if(index == -1) { 
      // Lazily associate instance with a new private object 
      instances.push(instance); 
      privates.push(privateObj = {}); 
     } 
     else { 
      // A privateObject has already been created, so grab that 
      privateObj = privates[index]; 
     } 
     return privateObj; 
    } 

    function Foo() { 
     _(this).bar = "This is a private bar!"; 
    } 

    Foo.prototype.getBar = function() { 
     return _(this).bar; 
    }; 

    return Foo; 
})(); 

Se dará cuenta de la función _ anteriormente. Esta es la función de acceso para agarrar el objeto privado. Funciona de forma perezosa, por lo que si lo llamas con una nueva instancia, creará un nuevo objeto privado sobre la marcha.

Si no desea duplicar el código _ para cada clase, puede resolver este envolviéndolo dentro de una función de fábrica:

function createPrivateStore() { 
    var instances = [], privates = []; 

    return function (instance) { 
     // Same implementation as example above ... 
    }; 
} 

Ahora se puede reducir a una sola línea para cada clase :

var _ = createPrivateStore(); 

una vez más, usted tiene que ser muy cuidadoso el uso de esta solución, ya que puede crear pérdidas de memoria si no se implementa y llama a un functi destruir encendido cuando sea necesario.

-1

He creado una nueva biblioteca para habilitar métodos privados en la cadena de prototipos. https://github.com/TremayneChrist/ProtectJS

Ejemplo:

var MyObject = (function() { 

    // Create the object 
    function MyObject() {} 

    // Add methods to the prototype 
    MyObject.prototype = { 

    // This is our public method 
    public: function() { 
     console.log('PUBLIC method has been called'); 
    }, 

    // This is our private method, using (_) 
    _private: function() { 
     console.log('PRIVATE method has been called'); 
    } 
    } 

    return protect(MyObject); 

})(); 

// Create an instance of the object 
var mo = new MyObject(); 

// Call its methods 
mo.public(); // Pass 
mo._private(); // Fail 
0

Personalmente, no me gusta mucho la solución con el GUID, ya que obliga al desarrollador a declarar que, además de la tienda y para incrementar en el constructor. En las aplicaciones de JavaScript grandes, los desarrolladores pueden olvidarse de hacerlo, lo que es bastante propenso a errores.

Me gusta la respuesta de Peter más o menos por el hecho de que puede acceder a los miembros privados usando el contexto (esto). Pero una cosa que me molesta bastante es el hecho de que el acceso a miembros privados se realiza en una o (n) complejidad. De hecho, encontrar el índice de un objeto en matriz es un algoritmo lineal. Considere que desea utilizar este patrón para un objeto instanciado 10000 veces. Luego, puede iterar a través de 10000 instancias cada vez que desee acceder a un miembro privado.

Para acceder a tiendas privadas en una complejidad o (1), no hay otra manera que usar guids.Pero a fin de no molestar con la declaración GUID y incrementación y con el fin de utilizar el contexto para acceder al almacén privado I modificado patrón de la fábrica Peters de la siguiente manera:

createPrivateStore = function() { 
var privates = {}, guid = 0; 

return function (instance) { 
    if (instance.__ajxguid__ === undefined) { 
     // Lazily associate instance with a new private object 
     var private_obj = {}; 
     instance.__ajxguid__ = ++guid; 
     privates[instance.__ajxguid__] = private_obj; 
     return private_obj; 
    } 

    return privates[instance.__ajxguid__]; 
} 

}

El truco aquí es tener en cuenta que los objetos que no tienen la propiedad ajxguid aún no se manejan. De hecho, uno podría establecer manualmente la propiedad antes de acceder a la tienda por primera vez, pero creo que no hay una solución mágica.

2

Con los navegadores modernos adoptando algunas tecnologías ES6, puede usar WeakMap para resolver el problema GUID. Esto funciona en EI11 y superiores:

// Scope private vars inside an IIFE 
var Foo = (function() { 
    // Store all the Foos, and garbage-collect them automatically 
    var fooMap = new WeakMap(); 

    var Foo = function(txt) { 
     var privateMethod = function() { 
      console.log(txt); 
     }; 
     // Store this Foo in the WeakMap 
     fooMap.set(this, {privateMethod: privateMethod}); 
    } 

    Foo.prototype = Object.create(Object.prototype); 
    Foo.prototype.public = function() { 
     fooMap.get(this).p(); 
    } 
    return Foo; 
}()); 

var foo1 = new Foo("This is foo1's private method"); 
var foo2 = new Foo("This is foo2's private method"); 
foo1.public(); // "This is foo1's private method" 
foo2.public(); // "This is foo2's private method" 

WeakMap no almacenará las referencias a cualquier Foo después de que se elimina o se cae fuera del ámbito, y puesto que utiliza objetos como llaves, no es necesario adjuntar a GUID tu objeto

+0

+1, pero si vas con ES6, ¿por qué no usar [símbolos] (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol) – Christoph

+0

@Christoph: porque los símbolos están unidos al objeto y puedes obtenerlos usando 'Object.getOwnPropertySymbols()'. Probablemente ya hayas descubierto esto ... – jimasun

Cuestiones relacionadas