Javascript es un lenguaje puramente orientado a objetos. En este artículo vamos a revisar las técnicas más recientes para la creación de objetos en Javascript. Gracias a estos patrones, podremos crear aplicaciones más organizadas, mantenibles y reusables.

Autor:

Hola! Soy programador web y arquitecto Javascript. Fundador de www.etnassoft.com donde escribo regularmente sobre JS y programación en general. Podéis seguirme en Twitter a través de @etnassoft.

Introducción

Javascript es un lenguaje puramente orientado a objetos. Sin embargo, en muchas ocasiones comenzamos a programar sin un patrón de diseño y llenamos nuestra aplicación de variables y funciones globales. Esto se considera una mala práctica y puede dar lugar a errores como las colisiones o dificultades para mantener y reusar el código.

En este artículo vamos a revisar las técnicas más recientes para la creación de objetos en Javascript. Gracias a estos patrones, podremos crear aplicaciones más organizadas, mantenibles y reusables.

Malas prácticas desde el día cero

Cuando comenzamos a programar en Javascript es frecuente declarar un sin fin de variables globales que más tarde iremos utilizando en nuestro script conforme las necesitemos.

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.

Para la mayoría de autores, se resume con la misma frase: hay que evitar la creación de variables y funciones (objetos en general) globales siempre que no sean absolutamente necesarios.

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 ); // Invocamos a la variable directamente por su nombre
console.log( window.foo ); // Invocamos a la variable como un método

No hay nada nuevo en todo esto. A partir de aquí, ya podemos buscar una estrategia para evitar el uso de variables globales. Para ello, lo ideal es crear un número pequeño de objetos que hagan de contenedores para estas variables o, lo que es lo mismo, recoger todo aquello global que participe de un mismo proceso dentro de un namespace (contexto) propio.

Autores como Douglas Crockford, John Resig, Angus Croll o James Edwards han elaborado sus propias aproximaciones a este modelo. Veamos algunas de ellas comentando sus ventajas e incovenientes…

Creación de un objeto global y asignación de métodos por prefijo

Es la opción más sencilla pero quizá algo desorganizada. En su favor, tenemos que es segura para refactorizar aquellos códigos que presentan un número considerable de variables globales haciendo difícil su reutilización en otros proyectos.

Tomemos el siguiente código:

// Opción mala (antipattern)
var foo = 'Hello';
var bar = 'World';
function sum( param1, param2 ){
return param1 + param2;
}
function myMessage(){
return foo + ' ' + bar;
}

console.log( sum( 10, 5 ) ); // 15
console.log( myMessage() ); // Hello World

En el ejemplo anterior, definimos dos variables globales y dos funciones dentro del namespace general. Como ya hemos visto, esta forma de programar puede traernos problemas de colisión además de que el código no presenta ningua cohesión.

Si quisiéramos reaprovechar parte del script, tendríamos que tener cuidado de no olvidarnos alguna variable o función durante el proceso. Según el método de asignación directa que comentamos, el código anterior quedaría como sigue:

var myApp = {}
myApp.foo = 'Hello';
myApp.bar = 'World';
myApp.sum = function( param1, param2 ){
return param1 + param2;
}
myApp.myMessage = function(){
return myApp.foo + ' ' + myApp.bar;
}

console.log( myApp.sum( 10, 5 ) ); // 15
console.log( myApp.myMessage() ); // Hello World

Hemos creado un único objeto global, myApp, 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 de portar el código, solo tendríamos que preocuparnos de coger aquellas funciones cuyo prefijo coincida con su contexto.

En el código anterior podríamos utilizar la referencia this en el interior de las funciones para facilitar el mantenimiento en el futuro, pero esto puede dar lugar a errores inesperados. Probemos por ejemplo a asignar uno de los métodos que hemos creado a una nueva función:

var newFunction = myApp.myMessage;
console.log( newFunction() ); // Hello World

Las referencias a las variables se conservan y nuestra nueva función accede a ellas siguiendo la ruta original (myApp.foo y myApp.bar).

Cambiemos ahora la referencia a las variables en el ejemplo original:

// ...
myApp.myMessage = function(){
return this.foo + ' ' + this.bar;
}
// ...
console.log( myApp.myMessage); // Hello World

El código continúa funcionando correctamente pero, si volvemos a asignar el método a una nueva función, el resultado varía:

var newFunction = myApp.myMessage;
console.log( newFunction() ); // Undefined. this está refiriendo al objeto global.

Este ejemplo viene a ilustrar una regla que cada vez más autores defienden: nunca debemos referenciar un objeto que estamos utilizando como namespace (contexto) con ‘this‘ ya que podemos obtener errores cuando importamos métodos de un contexto a otro.

Notación literal

Utilizando la notación literal, podemos evitar hacer referencia al objeto global cada vez que necesitemos crear un nuevo método. Además, conseguimos un código más limpio y ordenado. Partiendo del ejemplo anterior, el resultado sería:

var myApp = {
foo : 'Hello',
bar : 'World',
sum : function( param1, param2 ){
return param1 + param2;
},
myMessage : function(){
return this.foo + ' ' + this.bar;
}
};

console.log( myApp.sum( 10, 5 ) ); // 15
console.log( myApp.myMessage() ); // Hello World

Esta estrategia sería la más sencilla para la creación de una estructura básica de POO en Javascript. La portabilidad de los objetos resulta sencilla al tener ‘encapsulado’ todo el proceso.

Módulos

El patrón que llamamos módulo se configura a partir de una función que actúa como contexto para nuestra aplicación. Por lo general, esta función se autoejecuta devolviendo el objeto que representa la interfaz pública de nuestro módulo.

Este modelo es más familiar para aquellos que provienen de otros lenguajes de programación orientados a objetos al permitir distinguir entre métodos públicos y privados. Ya que Javascript no implementa el concepto de clases de forma nativa, creamos funciones que actúen como tales.

Todas las variables y funciones definidas en el interior de nuestra “clase” se consideran métodos privados. Aquellos métodos que queremos hacer públicos los devolvemos mediantes el comando “return” al ámbito general (módulo) de la aplicación.

var myApp = (function(){
var foo = 'Hello';
var bar = 'World';
var sum = function( param1, param2 ){
return param1 + param2;
}
return {
myMessage: function(){
return foo + ' ' + bar;
}
}
})();

console.log( myApp.myMessage() ); // Hello World
console.log( myApp.sum( 10, 5 ) ); // myApp.sum is not a function. sum es privada

En el código anterior, myApp.sum no se ha incluido en el objeto return por lo que permanece privada y visible únicamente dentro de su contexto. Sin embargo, myMessage, si que ha sido devuelta y añadida al entorno de myApp, por lo que funciona como se espera. Con este modelo, ya no es necesario referenciar a las variables con this, ya que comparten mismo contexto.

El aspecto negativo de este patrón es que accedemos de forma diferente a los métodos según sean públicos o privados. Esto supone que si queremos cambiar su visibilidad, tenemos que modificar el código moviendo funciones desde o hacia el método return. Otro inconveniente es que los métodos privados resultarán innacesibles para aquellas funciones que sean añadidas al objeto padre (módulo) con posterioridad a su definición.

Contexto dinámico

Si declaramos un entorno y lo pasamos como argumento de una función autoejecutable, evitamos tener que asignar variables y métodos al contexto mediante el comando return. Resulta por lo tanto una solución derivada de la anterior pero más legible e intuitiva.

var myApp = {};
(function( context ){
var foo = 'Hello';
var bar = 'World';
context.sum = function( param1, param2 ){
return param1 + param2;
};
context.myMessage = function(){
return foo + ' ' + bar;
}
})( myApp );

console.log( myApp.sum( 10, 5 ) ); // 15
console.log( myApp.myMessage() ); // Hello World

Las variables foo y bar, permanecen como privadas, por lo que solo tienen visibilidad dentro de su contexto. Los métodos asignados al objeto contenedor, se convierten en públicos.

Siguiendo este patrón, incluso podemos asignar el contexto al objeto global tal y como hace, por ejemplo, la librería jQuery. Esto permite al usuario elegir si los métodos de nuestra aplicación se convierten en globales o se restringen a la misma:

(function( context ){
var foo = 'Hello';
var bar = 'World';
context.sum = function( param1, param2 ){
return param1 + param2;
};
context.myMessage = function(){
return foo + ' ' + bar;
}
})( this );

console.log( sum( 10, 5 ) ); // 15
console.log( myMessage() ); // Hello World

Echemos ahora un vistazo al patrón de James Edwards…

Patrón de James Edwards ‘This namespaces proxy’

James Edwards ha creado un patrón cuya simpleza esconde una joya en cuanto a arquitectura de código. Utilizando la referencia this para inyectar métodos al contexto, evita que éstos puedan ser reasignados accidentalmente más adelante.

var myApp = {};
(function(){
var foo = 'Hello';
var bar = 'World';
this.sum = function( param1, param2 ){
return param1 + param2;
};
this.myMessage = function(){
return foo + ' ' + bar;
};
}).apply( myApp );

console.log( myApp.sum( 10, 5 ) ); // 15
console.log( myApp.myMessage() ); // Hello World

Simple y elegante: el uso de los comandos apply y call separan el contexto de los argumentos, lo que mejora aún más el diseño general. Siguiendo este patrón, podemos incluso asignar un mismo módulo a dos contextos direfentes consiguiendo implementaciones paralelas:

var context1 = {}, context2 = {};
var incrementNumber = function( param1 ){
var startNumber = param1 || 0;
this.increment = function(){
return startNumber++;
}
};
incrementNumber.call( context1 );
incrementNumber.call( context2, 10 );

console.log( context1.increment() ); // 0
console.log( context1.increment() ); // 1
console.log( context1.increment() ); // 2
console.log( context2.increment() ); // 10
console.log( context2.increment() ); // 11
console.log( context2.increment() ); // 12

Conclusión final

En la programación Javascript moderna, evitar la creación de variables y métodos globales resulta básico: si pretendemos integrar librerías de terceros o escribir un código que pueda ser reutilizado, tenemos que estructurarlo correctamente para evitar los errores derivados de las colisiones y ambigüedades.

Para ello, recurrimos a la creación de objetos que actúen como contenedores de aquellas piezas de código que compartan funcionalidad. A lo largo del artículo hemos visto diferentes aproximaciones al mismo problema analizando tanto sus ventajas como los inconvenientes.

Corresponde ahora al desarrollador elegir aquel método que mejor se adapte a sus necesidades concretas o crear su propio patrón a partir de los mismos.

Más información:

Os dejamos algunos enlaces para curiosos que quieran seguir profundizando en el tema:

Nos vemos en la próxima publicación… ¡Un saludo!

¿Necesitas desarrollar un proyecto web o para móviles? ¡Estamos disponibles!

Visitar Cokidoo

Cokidoo, los creadores de Ontuts, desarrollamos proyectos tecnológicos centrados en redes sociales y aplicaciones web, aplicaciones móviles y consultoría web y bases de datos.

Somos jóvenes, inquietos, versátiles, apasionados por la innovación y enfocados en las nuevas tecnologías. Con Ontuts tratamos de compartir nuestro conocimiento adquirido en los distintos proyectos, ayudando a la comunidad y mostrando nuestra capacidad tecnológica.

Si necesitas un presupuesto sin compromiso, estamos disponibles, no dudes en contactar con nosotros.

Comentarios en esta publicación (10 comentarios)

¿Te ha gustado esta publicación? ¡Puedes compartir tu opinión con todos nosotros! Simplemente pincha aquí mismo.

Me quito el sombrero Carlos, gran análisis.

Muy buen artículo, ya sabía que no dominaba Javascript, pero me doy cuenta que lo hago todo mal, jejej

Sergio Sosa

Increíble, la manera como muestras la flexibilidad de este lenguaje, felicidades por esta publicación, justo lo que estaba buscando

Buen articulo ;) , aunque en el ultimo ejemplo la función incrementNumber queda en el ámbito “Widnow” o global, lo cual se supone deveriamos evitar y ya luego se asigna, mientras menos variables tenga en global, es mejor, mi técnica regularmente es crear y modular los JS y sus acciones

proyecto={};
proyecto.varLocalDelProyecto;
proyecto.metodoAccesibleDesdeTodosLosModulos=function(){};
proyecto.usuario.varLocalAlModuloUsuario = “unValor”;
proyecto.usuario.metodoLocalAModuloUsuario = function(){};
proyecto.usuario.edicion={
variable:”unvalor”,
metodoExclusivoDeEdicion:function(){
var variableLocalAMetodo1;
},
init:function(){
}
}
proyecto.usuario.alta={}
proyecto.usuario.lista={}
….

..
.

Esto hace que los Script los script quede completamente modulados y las cosas q son comunes entre mas de un modulo o en toda la aplicación sean accesibles desde cualquier lugar, es una técnica q conocí por hay de un año y la calidad del scripting se incrementa en un nivel bastante considerable.
si gustan un reto interesane es esta aplicaciòn http://www.jslint.com/ básicamente evalúa que tu script tenga las “mejores practicas” del Scripting en JS, por esta aplicaciòn me di cuenta de muchas cosas que uno hace mal o que están bien pero caben en lo no recomendado

Hola fcodiaz!

El último ejemplo lo dejamos dentro del ámbito global ya que sólo pretendíamos ilustrar cómo asignar un módulos a varios contextos al mismo tiempo. Esto es, a día de hoy, la más moderna aproximación al desarrollo complejo de aplicaciones en Javascript. Queda para el programador establecer los contextos en los que quiere disfrutar de la funcionalidad de un módulo. Así, sería posible por ejemplo, implementar todos los métodos de una librería como jQuery dentro del contexto creado por otra segunda librería (por ejemplo una propia u otra del tipo Mootools).

Tú método es correcto; es el que hemos llamado Asignación de métodos por Prefijo al principio del artículo y es perfectamente válido para mantener un poco de orden. Sin embargo, es difícil para portar a otra aplicación y conservar la integridad en las referencias, por lo que hoy día, apenas se usa. Es mucho más interesante el paradigma del Módulo.

Completamente de acuerdo en que JSLint es una de las herramientas de cabecera para los desarrolladores de Javascript. Sin embargo, el lenguaje es más flexible de lo que muchas veces permite este validador, por lo que hay que saber utilizarlo en su justa medida.

Un saludo!

Ignacio

Clarísima explicación. Muchas gracias, Carlos.

Pues soy algo novato en JavaScript a comparación de ustedes pero si he programado y programo con el…mirando esto de los patrones en verdad que me gusto mucho como es la organización del mismo código se ve hasta bonito :D

me llamo mucho la atención el patrón de contexto dinámico creo que empezare a usar ese tipo de diseño en algunas cosas… :D

lokcito

Un semidios.!!. en el tema.
Me siento sucio de tanto codigo basura. que creaba.
Por fin vi la liberacion.!!!

Jorge Flores

Muy clara la explicación me convenciste! saludos

refreegrata

Tengo una duda. Si yo usara el patrón módulo, pero quisiera extenderlo, tal ves en otro script (añadiendo más funciones o namescpaces) como podría hacerlo? ¿Tendría que crear los namespaces y/o funciones y además agregarlos al return del namespace padre?.
Saludos.

Añade Tu Comentario