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