2009-06-03 17 views
10

Estoy tratando de entender la clase de clases, visibilidad de datos y cierres (específicamente en Javascript) y estoy en la página de documentos jQuery para tipos, menciona que los cierres se usan para ocultar datos :Cierre de Javascript y visibilidad de datos

El patrón le permite crear objetos con métodos que operan con datos que no son visibles desde el exterior, la base misma de la programación orientada a objetos.

El ejemplo:

function create() { 
    var counter = 0; 
    return { 
    increment: function() { 
     counter++; 
    }, 
    print: function() { 
     console.log(counter); 
    } 
    } 
} 
var c = create(); 
c.increment(); 
c.print(); // 1 

Al declarar la variable contador con la palabra clave var, que ya está en el ámbito local dentro de la definición de la función/clase. Por lo que sé y puedo decir, no es accesible desde el exterior para empezar. Me estoy perdiendo algo desde una perspectiva de visibilidad de datos.

En segundo lugar, hay una ventaja para escribir la clase como la de arriba frente, como a continuación:

function create() { 
    var counter = 0; 
    this.increment = function() { 
     counter++; 
    } 
    this.print = function() { 
     console.log(counter); 
    } 
    return this; 
} 
var c = create(); 
c.increment(); 
c.print(); // 1 

Como yo lo entiendo, estos son más o menos semánticamente lo mismo - la primera es sólo más "estilo jQuery ". Me pregunto si hay una ventaja u otro matiz que no aprecio del primer ejemplo. Si estoy en lo cierto, ambos ejemplos crean cierres en el sentido de que están accediendo a datos declarados fuera de su propio alcance.

http://docs.jquery.com/Types#Closures

Respuesta

8

Antes que nada, tiene razón en que ambas versiones usan cierres.

La primera versión es más limpia (en mi opinión) y más popular en javascript moderno. El mayor inconveniente potencial del primer estilo es que no se pueden asignar objetos al prototipo del constructor, lo cual es útil (y más eficiente) si está creando muchos de los mismos objetos.

El segundo estilo, nunca he visto en producción Javascript. Normalmente, se instanciar create con new, en vez de volver this en la función create(), así:

function create() { 
    var counter = 0; 
    this.increment = function() { 
     counter++; 
    } 
    this.print = function() { 
     console.log(counter); 
    } 
} 
var c = new create(); 
c.increment(); 
c.print(); // 1 
+0

Y personalmente, esta es la forma en que prefiero crear objetos, después de haber probado innumerables técnicas. Creo que esto es mucho más limpio y fácil de entender. Pero ese soy yo. :) –

+3

Minit nit: devolver "this" es equivalente a no devolver nada explícitamente, cuando se usa el nuevo operador. Sin el nuevo operador, "este" es el contexto actual (a menudo veces global), y así terminas destrozando variables globales. – ken

1

Su segundo ejemplo no siguen utilizando cierres, ya que las funciones de incremento y de impresión siguen actuando sobre una variable de otro modo está fuera del alcance — por la vez que se llama a crear c.increment() la función ya ha salido.

me gusta el primer ejemplo ya que evita la "this" palabra clave, y en javascript "this" puede ser complicado — que no siempre se refiere a lo que parece como debería.

+0

Estoy bastante seguro de que el hecho de usar la variable en la función anónima significa que persistirá, aunque la función haya salido y normalmente esa variable se descartaría. – Dave

2

Bueno, no me importa a entrar en una guerra religiosa sobre cómo crear objetos en JavaScript, ya algunas personas creen firmemente que hay una forma correcta e incorrecta de hacerlo.

Sin embargo, quiero señalar algo en su segundo conjunto de código que no es demasiado sabrosa, es decir, el hecho de que está asignando cosas nuevas propiedades en el objeto contenido en la palabra clave this, ¿se da cuenta de lo que objeto es?No es un objeto vacío a menos que utilice la sintaxis de instancias como esta:

var c = new create(); 

Cuando se hace esto, el this palabra clave dentro del cuerpo de la función constructora se le asigna un nuevo objeto de la marca, como si la primera línea en el cuerpo fuese algo así como:

this = {}; 

Pero cuando se llama create() como una función, como lo hace en ese ejemplo, que están alterando el alcance fuera de la definición de la función (como se alude a por @seanmonster en los comentarios)

+0

A menos que asigne la función a un prototipo u otro objeto, "este" es realmente el objeto global (ventana). – seanmonstar

+0

Hee hee, mi mal - la gramática de su comentario es tan sutil y lo tomé para decir algo opuesto a lo que decía. Lo siento por eso. Tengo que salir de estos medicamentos extraños ... –

+0

Agradezco su respuesta, aunque suele ser cierta, para ser más precisos, como he reflejado en mi respuesta editada ahora, la palabra clave podría ser algo más que el objeto global, dependiendo en donde se encuentra la definición de la función. Si su función "crear" estuviera anidada dentro de una función constructora, que es completamente válida, esta palabra clave sería diferente al alcance global. Entonces, de nuevo, le agradezco que lo haya mencionado porque puede ser complicado ... –

1

Christian Heilmann tiene un artículo bastante decente en the module pattern que usted describe que podría ayudarle a entenderlo y por qué es útil.

2

debe comparar el ejemplo contra este fragmento

function create() { 
    this.counter = 0; 
    this.increment = function() { 
     this.counter++; 
    }; 
    this.print = function() { 
     console.log(counter); 
    } 
} 

var c = new create(); 
c.increment(); 
c.print(); // 1 

Así que cuando nueva creación() es llamado inicializa el nuevo objeto con dos métodos y una variable de instancia (es decir: el contador). Javascript no tiene encapsulación per se por lo que podría acceder c.counter, de la siguiente manera:

var c = new create(); 
c.increment(); 
c.counter = 0; 
c.print(); // 0 

Mediante el uso de cierres (como se muestra en los ejemplos) contador es ahora ya un campo de instancia, sino más bien una variable local. Por un lado, no puede acceder desde fuera de la función create(). Por otro lado, increment() e print() pueden acceder porque cierran sobre el alcance adjunto. Así que terminamos con una muy buena emulación de encapsulación objetiva.

3

Al declarar la variable contador con la palabra clave var, que ya es localmente con ámbito dentro de la definición de la función /clase. Por lo que sé y puedo decir , no es accesible desde el exterior para comenzar. ¿Me falta algo desde una perspectiva de visibilidad de datos .

No es que la variable counter no es accesible desde fuera de la función, para empezar, es que es accesible a los increment y print funciones después de create función ha salido que hace closures tan útil.

+0

+1 por responder a la parte de la pregunta que todos los demás están evitando por alguna razón –

0

En su segundo ejemplo, cuando llama al create(), dentro del alcance de la función, this es el objeto global (que siempre es el caso cuando llama a una función "vacía", sin usarlo como constructor ni acceder a él) como una propiedad (por ejemplo, una llamada de "método")). En los navegadores, el objeto global es window. Así que cuando se llama a crear ocasiones posteriores, crea nuevos cierres, pero luego asignarlos a un mismo objeto global como antes, sobrescribir las funciones, que no es lo que quiere:

var c = create(); // c === window 
c.increment(); 
c.print(); // 1 
var c2 = create(); // c2 === c === window 
c.print(); // 0 
c2.print(); // 0 
increment(); // called on the global object 
c.print(); // 1 
c2.print(); // 1 

Las soluciones, como los demás han señalado, es usar new create().

1

Esta sintaxis tiene más sentido para mí con una formación en programación orientada a objetos:

Create = function { 
    // Constructor info. 

    // Instance variables 
    this.count = 10; 
} 

Create.prototype = { 

    // Class Methods 
    sayHello : function() { 
      return "Hello!"; 
    }, 

    incrementAndPrint : function() { 
      this.count++; 

      // Inner method call. 
      this.print(); 
    }, 

    print : function() { 
     return this.count; 
    } 


} 

var c = new Create(); 
alert(c.incrementAndPrint()); 
0
MYAPP = (function(){ 
    var v1,v2; 
    return { 
    method1:function(){}, 
    method2:function(){} 
    }; 
})(); 

siempre uso cierres como esto en mi solicitud, ya que hacer esto, todos mis propios métodos definidos están en espacio de nombres MIAPL , solo se puede acceder a v1 y v2 mediante métodos en MYAPP. En mi aplicación, a menudo solo escribo un archivo "app.js", con todos mis códigos js dentro. Supongo que puede definir un método llamado "registy" para definir una variable privada en MYAPP, luego puede usarlo en sus métodos. Todas las variables y métodos adicionales deben definirse por el método de registro cuando desee agregar códigos adicionales en el archivo html, al igual que el método JQuery.extend. He oído que si usa demasiados cierres en el navegador IE, es fácil que se desborde la pila. (En mi opinión)