Espacios de Nombres en Javascript

¡Saludos visitante! si deseas comentar o hacer una pregunta sobre este post por favor dirígete a la nueva dirección en http://variabletecnica.com.ve. La página que estás leyendo dejará de estar disponible el 15/11/2015. Gracias, y disculpa las molestias 🙂 En mi artículo anterior, Clases y Objetos en Javascript, mencioné algunas características de Javascript que me hicieron…


¡Saludos visitante! si deseas comentar o hacer una pregunta sobre este post por favor dirígete a la nueva dirección en http://variabletecnica.com.ve. La página que estás leyendo dejará de estar disponible el 15/11/2015. Gracias, y disculpa las molestias 🙂

En mi artículo anterior, Clases y Objetos en Javascript, mencioné algunas características de Javascript que me hicieron difícil iniciarme en el lenguaje. En el artículo de hoy hablaré sobre una forma de evitar uno de los primeros problemas que tuve: el «desorden del ámbito global» (global scope clutter).

Cuando vez artículos técnicos sobre recomendaciones para programar (indentación, convenciones de nombres, uso de comentarios…) una de las recomendaciones más comunes es evitar el uso de variables globales. Hay varias razones para esto, pero personalmente creo que las más importantes son: la organización del código en «unidades lógicas» en lugar de dejar variables «regadas» por todo el código, y evitar confundir una variable por otra con nombre similar o igual que hayas definido en otro lado.

Esto último es un problema en Javascript: por ser un lenguaje débilmente tipado está permitido asignar cualquier tipo de valor a cualquier variable, además permite declarar más de una vez la misma variable. Si declaras muchas variables globales corres el riesgo de declarar accidentalmente la misma variable global dos veces en bloques de código no relacionados, incluso en diferentes archivos.

Los Espacios de Nombres representan una solución a estos problemas, permitiendo que declares variables agrupadas en los «bloques» proporcionados por los espacios de nombres.

Espacios de Nombres

Para empezar, Javascript no tiene soporte para espacios de nombres. Así es, pero esto no es problema ya que podemos simularlos creando objetos que serán usados como «contenedores» de variables, veamos como.

Supongamos que estamos programando una sencilla aplicación web que permite cargar y modificar un organigramas, y tenemos el siguiente código en un archivo Javascript:

//Algunas variables globales
var nProfundidadMaxima = 5; 
var cTipoRepresentacion = 'HTML';
var lIncluirArepas = true;

//Función constructora de la clase Elemento
function Elemento(oValor){
  //Código de Inicialización... 
};
//Agrega un hijo al Elemento actual
Elemento.prototype.agregarHijo = function(oHijo){
  //Código para agregar un Elemento hijo... 
};
//Devuelve una cadena con la representación HTML del Elemento actual
Elemento.prototype.generarHTML = function(){
  //Código para generar HTML... 
  return cHtmlGenerado;
};
// Función constructora de la clase Diagrama
function Diagrama(){
  //Código de Inicialización... 
};
//Genera una nueva estructura para el diagrama actual 
//a partir de la representación de cadena indicada.
Diagrama.prototype.leerDiagrama = function(cDiagrama){
  //Código de lectura y generación... 
};
//Devuelve una cadena con la representación HTML del Diagrama actual
Diagrama.prototype.dibujarDiagrama = function(oSalida){
  //Código para generar HTML... 
  return cHtmlGenerado;
};
function actualizarDiagramas(){
  //Código para actualizar códigos en pantalla 
};
function prepararDesayuno(){
  //Aquí van las arepas :)
};

Como podemos ver tenemos: tres variables, dos funciones constructoras y dos funciones comunes, todas ellas en el ámbito global (Nota: las «funciones constructoras» y las «funciones comunes» solo se diferencian entre sí por su intensión, no por que tengan alguna característica especial).

En un proyecto pequeño esto no sería problema, pero si vas a crear mucho código Javascript o usar librerías de terceros, es conveniente usar un espacio de nombres para agruparlas. Hay varias formas de hacerlo, pero básicamente todo se reduce a crear un objeto en el ámbito global y crear todo lo demás como propiedades de ese objeto. Veamos:

//Espacio de Nombres
var diagramador = {};

//Las variables ahora pertenecen al espacio de nombres
//y ya no requieren "var"
diagramador.nProfundidadMaxima = 5; 
diagramador.cTipoRepresentacion = 'HTML';
diagramador.lIncluirArepas = true;

//Ahora la clase Elemento se define como
//una función en el espacio de nombres
diagramador.Elemento = function (oValor){
  //Código de Inicialización... 
};
//OK, esta línea ahora es más larga, pero vale la pena
diagramador.Elemento.prototype.agregarHijo = function(oHijo){
  //Código para agregar un Elemento hijo... 
};
//Devuelve una cadena con la representación HTML del Elemento actual
diagramador.Elemento.prototype.generarHTML = function(){
  //Código para generar HTML... 
  return cHtmlGenerado;
}
//Función constructora de la clase Diagrama
diagramador.Diagrama = function (){
  //Código de Inicialización... 
};
//Genera una nueva estructura para el diagrama actual 
//a partir de la representación de cadena indicada.
diagramador.Diagrama.prototype.leerDiagrama = function(cDiagrama){
  //Código de lectura y generación... 
}
//Devuelve una cadena con la representación HTML del Diagrama actual
diagramador.Diagrama.prototype.dibujarDiagrama = function(oSalida){
  //Código para generar HTML... 
  return cHtmlGenerado;
};
//Estas dos funciones no pertenece a las clases Elemento o Diagrama
//así que las definimos en la raíz del espacio de nombres
diagramador.actualizarDiagramas = function (){
  //Código para actualizar códigos en pantalla 
};
diagramador.prepararDesayuno = function(){
  //Arepas mejoradas, ahora con mantequilla y queso!
  //lo siento, estoy a dieta y tengo hambre :'(
};

Si abres la consola Javascript y ejecutas console.log(diagramador), verás que lo que antes estaba en el ámbito global ahora está contenido en un único objeto.

Otras formas de crear Espacios de Nombres

En la sección anterior creamos un objeto vacío y luego agregamos propiedades y funciones a medida que eran definidas.

También podríamos haber definido las propiedades y funciones en el espacio de nombres con un único literal de objeto, obteniendo el mismo efecto:

//Espacio de Nombres
var diagramador = {
  nProfundidadMaxima : 5,
  cTipoRepresentacion : 'HTML',
  lIncluirArepas : false,
  Elemento : function (oValor){
    //Código de Inicialización... 
  },
  Diagrama : function (){
    //Código de Inicialización... 
  },
  actualizarDiagramas : function (){
    //Código para actualizar códigos en pantalla 
  },
  prepararDesayuno : function(){
    //Sin arepas, a comer más frutas!
  }
};

//Las siguientes funciones deben quedar por fuera, 
//en mi artículo anterior explico porqué.
diagramador.Elemento.prototype.agregarHijo = function(oHijo){
  //Código para agregar un Elemento hijo... 
};
diagramador.Elemento.prototype.generarHTML = function(){
  //Código para generar HTML... 
  return cHtmlGenerado;
};
diagramador.Diagrama.prototype.leerDiagrama = function(cDiagrama){
  //Código de lectura y generación... 
};
diagramador.Diagrama.prototype.dibujarDiagrama = function(oSalida){
  //Código para generar HTML... 
  return cHtmlGenerado;
};

Como último ejemplo, podemos usar una función auto-ejecutable al crear el espacio de nombres, con lo que ahora contamos con la posibilidad de crear funciones y variables privadas, que solo serán accesibles desde dentro de las funciones del espacio de nombres:

//Espacio de nombres
var diagramador = (function(){

  //Definición de variables y funciones privadas
  var lActivarContador = true;
  var nContadorPrivado = 0;
  var incrementarContador = function(){/* ... */};
  var resetearContador = function(){/* ... */};


  //Definición interna del espacio de Nombres
  var oLocal = {
    nProfundidadMaxima : 5,
    cTipoRepresentacion : 'HTML',
    lIncluirArepas : false,
    Elemento : function (oValor){/* ... */},
    Diagrama : function (){/* ... */},
    actualizarDiagramas : function (){/* ... */},
    prepararDesayuno : function(){/* ... */}
  };

  //Las siguientes funciones deben quedar por fuera, 
  //en mi artículo anterior explico porqué.
  oLocal.Elemento.prototype.agregarHijo = function(oHijo){/* ... */};
  oLocal.Elemento.prototype.generarHTML = function(){/* ... */};
  oLocal.Diagrama.prototype.leerDiagrama = function(cDiagrama){/* ... */};
  oLocal.Diagrama.prototype.dibujarDiagrama = function(oSalida){/* ... */};
  
  //Y finalmente devolvemos el espacio de nombres local 
  return oLocal;

})();

Luego de ejecutar este código puedes abrir la consola Javascript y ejecutar console.log(diagramador), y verás el mismo resultado que en los ejemplos anteriores, con el beneficio adicional de que las variables y funciones privadas no son accesibles a través del espacio de nombres.

Notas Adicionales

Los ejemplos presentados en este artículo solo usaron un espacio de nombres, pero dependiendo del tamaño y la complejidad de tu proyecto tal vez necesites usar más de uno. Por ejemplo, si desarrollas un conjunto de funciones para el diagramador, y por separado otro conjunto de funciones para un panel de noticias en la misma aplicación, sería conveniente crear dos espacios de nombres separados. Esto sería especialmente útil si existe la posibilidad de reutilizar alguno de estos componentes en otra aplicación.

Otra posibilidad podría ser utilizar un espacio de nombres central, como miEmpresa y crear espacios de nombres especializados dentro de ella, como miEmpresa.diagramador y miEmpresa.servicioNoticias. Claro sin exagerar, la idea es organizar y jerarquizar los componentes de tu aplicación, no «esconderlos» bajo varios niveles de espacios de nombres.

Derecho de uso

Los contenidos generados por el autor de este artículo (explicaciones, código fuente, y archivos adjuntos creados por el autor) están disponibles bajo licencia CC BY-SA 3.0, y pueden ser usados, derivados y compartidos bajo los términos indicados en la misma. Los contenidos no generados por el autor de este artículo son propiedad de sus respectivos dueños y están regidos por las licencias que estos hayan dispuesto.

Cita del día

What’s in a name? That which we call a rose by any other name would smell as sweet.
William Shakespeare

Soluciones claras y simples



Ing. Industrial, dedicado a la programación en ASP.NET+VB, SQL y Javascript+AJAX; y un poco de Android, Kotling, y Unity 🙂
Valencia, Venezuela



¿QUIERES APOYARME?

¿Te ha sido de ayuda alguno de mis artículos? Generar contenido técnico requiere de tiempo y esfuerzo. Con tu colaboración me puedes ayudar a mantener mi blog activo y actualizado. Si quieres y puedes apoyarme has clic aquí:

https://paypal.me/roimergarcia


ENTRADAS RECIENTES