2009-07-25 12 views
8

¿Hay algún inconveniente al usar una "clase" de JavaScript con este patrón?"clases" JavaScript

var FooClass = function() 
{ 
    var private = "a private variable"; 
    this.public = "a public variable"; 

    var privatefn = function() { ... }; 
    this.publicfn = function() { ... }; 
}; 

var foo = new FooClass(); 
foo.public = "bar"; 
foo.publicfn(); 

Respuesta

14

Lo que está haciendo en su ejemplo no es el patrón de "clase" que la gente piensa en JS: normalmente las personas piensan en el modelo de clase más "normal" de Java/C#/C++/etc que puede fingido con las bibliotecas.

En vez tu ejemplo es en realidad bastante normal y buen diseño JS, pero para la integridad voy a discutir las diferencias de comportamiento se podrán ver entre los "miembros" privadas y públicas que tiene

var private = "a private variable"; 
this.public = "a public variable"; 

Acceso private desde dentro cualquiera de sus funciones será mucho más rápida que acceder al public porque la ubicación de private se puede determinar razonablemente bien solo con una búsqueda estática del motor JS. Los intentos de acceder al public requieren una búsqueda, la mayoría de los motores JS modernos realizan cierto grado de almacenamiento en la memoria caché de búsqueda, pero aún así es más costoso que un simple acceso var variado.

var privatefn = function() { ... }; 
this.publicfn = function() { ... }; 

Las mismas reglas de búsqueda se aplican a estas funciones al igual que con accede la variable anterior, la única diferencia real (en su ejemplo) es que si sus funciones se llaman, dicen privatefn() vs this.publicfn(), privatefn siempre obtendrá lo global objeto para this. Pero también si alguien hace

f = foo.publicfn; 
f(); 

A continuación, la llamada a f tendrá el objeto global como this pero será capaz de modificar la variable private.

La forma más normal de hacer funciones públicas sin embargo (que resuelve la función pública separada que modifica el problema de miembros privados) es poner funciones públicas en el prototipo, por ejemplo.

Foo.prototype.publicfn = function() { ... } 

lo que obliga a las funciones públicas de no modificar la información privada - hay algunas veces que esto no es una opción, pero es una buena práctica ya que también reduce el uso de memoria un poco, tomar:

function Foo1() { 
    this.f = function(){ return "foo" }; 
} 

vs

function Foo2() { 
} 
Foo2.prototype.f = function(){ return "foo" }; 

En Foo1 tiene una copia del objeto de la función para cada instancia de Foo1 (no todo el Emory, sólo el objeto, p.ej. new Foo1().f !== new Foo2().f) mientras que en Foo2 solo hay un objeto de función único.

3

Eso es bueno hasta ahora, pero hay otro nivel de acceso que has omitido.

this.publicfn es realmente un método privado ya que tiene acceso a miembros y funciones privados.

Para añadir métodos que son públicos, pero no priveleged, modificar el prototipo de la siguiente manera:

FooClass.prototype.reallypublicfn = function() { ... }; 

nota que este método no tiene acceso a los miembros privados de FooClass pero es accesible a través de cualquier instancia de FooClass.

Otra forma de lograr esto está volviendo estos métodos desde el constructor

var FooClass = function() 
{ 
    var private = "a private variable"; 
    this.public = "a public variable"; 

    var privatefn = function() { ... }; 
    this.publicfn = function() { ... }; 

    return { 
    reallypublicfn: function() { ...} 
    } 
}; 

var foo = new FooClass(); 
foo.public = "bar"; 
foo.publicfn(); 

Básicamente, estos métodos de ocultar datos de ayuda que se ajusten a las técnicas de programación orientada a objetos tradicionales. En términos generales, mejorar la ocultación de datos y la encapsulación en las clases es algo bueno. Asegurar un bajo acoplamiento hace que sea mucho más fácil cambiar las cosas en el camino, por lo que exponer públicamente lo menos posible es realmente para su beneficio.

Consulte https://developer.mozilla.org/en/Introduction_to_Object-Oriented_JavaScript para obtener una descripción simple y http://www.crockford.com/javascript/private.html para obtener detalles sobre cómo lograr estas cosas.

+1

Su ejemplo FooClass es incorrecto. La semántica de new es efectiva: "var foo = {}; foo .__ proto__ = FooClass.prototype; var temp = FooClass.call (temp); if (IsObject (temp)) foo = temp;". Esto significa que en su ejemplo foo será el nuevo objeto "{reallypublicfn: function() {...}}" para que no tenga el prototipo FooClass o la función publicfn – olliej

3

El principal inconveniente es que terminará con una copia de publicfn para cada instancia de FooClass. Si va a crear una gran cantidad de objetos FooClass, sería más eficiente para escribir

FooClass.prototype.publicfn = function() { ... }; 
+1

tenga en cuenta que esas dos formas de crear publicfn no son equivalentes . uno es priveleged y el otro no es –

1

Depende de sus necesidades y el rendimiento relativo. Javascript no es el lenguaje más seguro y no es muy sólido con respecto a la visibilidad de los miembros. Tradicionalmente puede tener visibilidad "privada", "privilegiada públicamente" y "pública" dentro de un tipo de Javascript.

Puede declarar miembros privilegiados públicas y privadas usando:

function FooClass() 
{ 
    var privateVar = 1; 
    function privateFn() 
    { 
     return privateVar; // etc... 
    } 
    this.publicVar = 2; 
    this.publicFn = function() 
    { 
     return privateFn(); 
    } 
} 

En este ejemplo se utiliza un cierre de función, que consiste en una declaración de función que incluye los valores del ámbito donde se define la función. Esto es aceptable cuando la visibilidad de los miembros es necesaria pero puede generar gastos generales. El intérprete de JavaScript no puede reutilizar las definiciones de privateFn o publicFn para cada instanciación ya que se refieren a variables o funciones en el ámbito externo. Como resultado, cada instancia de FooClass da como resultado un espacio de almacenamiento adicional para privateFn y publicFn. Si el tipo se usa con poca frecuencia o con moderación, la penalización de rendimiento es neglegible. Si el tipo se usa con mucha frecuencia en la página, o si la página tiene un estilo "AJAX" en el que la memoria no se libera con tanta frecuencia debido a que la página no se descarga, la penalización puede ser más visible.

Un enfoque alternativo es usar miembros prototipo. Estos son miembros públicos sin privilegios. Como Javascript no es del todo seguro para el tipo y es relativamente fácil de modificar después de cargarlo, la seguridad del tipo y la visibilidad de los miembros no son tan confiables para controlar el acceso a las partes internas del tipo. Por razones de rendimiento, algunos marcos como ASP.NET Ajax en cambio usan nombres de miembros para inferir reglas de visibilidad. Por ejemplo, el mismo tipo de ASP.NET Ajax podría ser:

function FooClass2() 
{ 
    this._privateVar = 1; 
    this.publicVar = 2; 
} 
FooClass2.prototype = 
{ 
    _privateFn : function() 
    { 
     return this._privateVar; 
    }, 
    publicFn : function() 
    { 
     return this._privateFn(); 
    } 
} 
FooClass2.registerClass("FooClass2"); 

En este caso, los miembros con ámbito privadas son privadas sólo de nombre, el "_" prefijo se considera en el sentido de una variable miembro privada. Tiene la desventaja de evitar que el miembro sea realmente privado, pero el lado positivo de permitir el parche en memoria del objeto. El otro beneficio principal es que todas las funciones son creadas una vez por el intérprete y el motor y reutilizadas una y otra vez para el tipo. La palabra clave "this" se refiere a la instancia del tipo aunque la referencia de la función sea la misma.

Una forma de ver la diferencia en la acción es tratar esto con ambos tipos (si usted no tiene ASP.NET Ajax, puede ignorar la última línea de FooClass2 que llama registerClass())

var fooA = new FooClass(), fooB = new FooClass(); 
alert(fooA.publicFn===fooB.publicFn); // false 

var foo2A = new FooClass2(), foo2B = new FooClass2(); 
alert(foo2A.publicFn===foo2B.publicFn); // true 

Así que es una cuestión de seguridad de tipo y visibilidad de miembros vs.el rendimiento y la capacidad de parchear en la memoria

0

Además, si se me permite mencionar algo útil para esto - Con prototype que son capaces de añadir métodos adicionales más adelante en el código para ampliar la función de un ámbito externo. Como pequeño beneficio, el cuerpo entero con todos los métodos no se renderizará cada vez, por lo tanto, acelera la compilación de actuaciones. Si ya tiene todos los métodos declarados dentro de la función, esto requerirá más tiempo para renderizar. Por lo tanto, mi consejo es que los apliquen más adelante solo cuando se vuelvan relevantes en el código actual.

Ejemplo:

// inside 
function fn(arg) { 
    this.val = arg; 
    fn.prototype.getVal =()=> { 
     console.log(this.val); 
    } 
} 
var func = new fn('value'); 
func.getVal(); 


// declare extern methods 
function fn2(arg) { 
    this.val = arg; 
} 
fn2.prototype.getVal =()=> { 
    console.log(this.val); 
} 
var func2 = new fn2('value'); 
func2.getVal(); 
Cuestiones relacionadas