viernes, 25 de noviembre de 2011

Namespaces en javascript (1 de 2)

Según la wikipedia, javascript se define como un lenguaje de programación orientado a objetvos, basado en prototipos, imperativo y débilmente tipado. Se utiliza principalmente en su forma del lado del cliente (client-side), implementado como parte de un navegador web permitiendo mejoras en la interfaz de usuario y páginas web dinámicas, aunque existe una forma de JavaScript del lado del servidor (Server-side JavaScript o SSJS). Su uso en aplicaciones externas a la web, por ejemplo en documentos PDF, aplicaciones de escritorio (mayoritariamente widgets) es también significativo.



Cuando comenzamos a programar en javascript sin tener mucha idea de lo que hacemos, es frecuente declarar  variables globales sin ton ni son, que luego iremos usando según nuestra necesidades. Sin embargo, la bibliografía moderna nos recomienda reservar el uso de este tipo de variables a aquellos objetos que tienen un impacto general en el entorno de nuestra aplicación. Evitando las variables globales reducimos el riesgo de colisión entre ellas a la vez que evitamos ambigüedades.

Resumiento, hay que evitar la creacion de variables y funciones globales, salvo que sea absolutamente necesario.

Para ilustrar esto, hay que recordar que en Javascript, cualquier variable global se asigna inmediatamente a un namespace (contexto) general: el objeto window. Esto permite que podamos acceder a ella directamente por su nombre, o como una propiedad del objeto general.
var foo = 'bar'; // Definimos la variable como global  
console.log( foo ); // Usamos la variable directamente por su nombre
console.log( window.foo ); // Usamos la variable como un método

Partiendo de la premisa anterior vemos que se hace necesario buscar alguna estrategia para evitar el uso de variables globales. Para ello, lo ideal es crear pequeños objetos que encapsulen estas variables globales. La primera aproximación que se nos puede ocurrir es la siguiente

var myContext = {}  

myContext.foo = 'Foo';  
myContext.bar = 'Bar';  
myContext.getMessage = function() {  
  return myContext .foo + ' ' + myContext .bar;  
}  
 
console.log( myApp.getMessage () ); // Foo Bar

Hemos creado un único objeto global, myContext, que utilizamos como contenedor para el resto de variables y funciones. De este modo, ganamos legibilidad al poder identificar de un solo vistazo, aquellas partes de código que trabajan conjuntamente. A la hora reutilizar el código, sólo tendríamos que preocuparnos de llevarnos aquellas funciones cuyo prefijo coincida con su contexto.

Otra forma de definir nuestro objeto global es mediante la notación literal. Con esta forma podemos evitar hacer referencia a dicho objeto cada vez que necesitemos crear un nuevo método. Partiendo del ejemplo anterior, el resultado sería:

var myContext = {  
  foo : 'Foo',  
  bar : 'Bar',  
  getMessage : function() {  
    return this.foo + ' ' + this.bar;  
  }  
};

Como vemos es una forma muy sencilla y directa de declarar objetos.

Existe otro patrón llamado módulo. Este patrón se configura a partir de una función que actua como contexto dentro de nuestra aplicación. Este función se autoejecuta y devuelve el objeto que representa la interfaz pública. Este módelo se asemeja a la programación clásica orienta a objetos ya que distingimos entre métodos públicos y métodos privados ya que como sabemos javascript no implementa el concepto de clases de forma nativa, sino que creamos funciones que actuan como tales.

Para nuestro ejemplo la declaración sería de la siguiente manera
var myContext = (function() {
  var foo = 'Foo';
  var bar = 'Bar';

  var privateMethod = function() {
    return 'Private Method';
  }

  return {
    getMessage: function() {
      return foo + ' ' + bar;   
    }
  }
})();
console.debug(myContext.privateMethod()); // Uncaught TypeError: Object #<Object> has no method 'privateMethod'
console.debug(myContext.getMessage()); // Foo Bar

Este este ejemplo el método 'privateMethod' no se ha incluido en el objeto return por lo que permanece oculto y visible únicamente dentro de su contexto. Vemos que no hace falta referenciar las variables 'foo' y 'bar' con this ya que estás comparten el contexto.

Uno de los problemas que plantea este patrón es que accedemos de manera diferente a los métodos según sean públicos o privados.

var myContext = (function() {
  var foo = 'Foo';
  var bar = 'Bar';

  var getFoo = function() {
    return foo;
  }

  var getBar = function () {
    return bar;
  }

  return {
    join: function () {
      return getFoo() + " " + getBar();
    },

    getMessage: function() {
      return join();      // ERROR: Uncaught ReferenceError: join is not defined
      return this.join(); // CORRECTO
    }
  }
})();

En el ejemplo anterior la llamada al método join desde el método getMessage nos da un error. Para hacer la llamada correctamente debemos usar this. Esto no ocurre con las llamadas a los métodos 'getFoo' y 'getBar' desde el método 'join'.

Con esto acabamos la introducción a los namespace en javascript. Para la próxima dejaremos el contexto dinámico y el patrón proxy de James Edwards

No hay comentarios:

Publicar un comentario