Introducción
Una de los requisitos cada día más común es que nuestro código Javascript debe poder funcionar sin problemas tanto en el navegador como en el servidor con Node. Esto puede parecer en principio algo sencillo, pero lo cierto es que en ocasiones nos encontraremos con limitaciones difíciles de salvar: utilizamos el DOM del navegador, requerimos algún recurso, como una base de datos, que sólo tenemos disponible en el servidor. En otros casos nuestro programa se puedrá ejecutar sin demasiadas dificultades tanto en el cliente como en el servidor al ser agnóstico al contexto de ejecución.
Sobre la base de esta premisa, con este artículo iniciamos una serie dedicada a mostrar algunos aspectos que consideramos interesantes en la construcción de un módulo en Javascript. Como ejercicio (prácticamente es una excusa) hemos seleccionado un problema sencillo, pero que a su vez recoge una complejidad intrínseca del lenguaje: la creación de una función universal que permita comparar cualquier tipo de dato en Javascript.
Aunque pueda parecer que comparar dos valores es algo sencillo y común, lo cierto es que comparar valores en Javascript conlleva algunas dificultades que debemos conocer y manejar para evitar problemas. Comparar valores como números o cadenas no es muy difícil, pero la cosa se complica cuando se producen conversiones de tipo no previstas o cuando queremos trabajar con fechas, matrices o cualquier tipo de objeto. Durante esta serie de artículos vamos a ver paso a paso que tenemos que tener en cuenta para hacer una correcta comparación en Javascript, desde lo más sencillo, hasta lo más complicado y de esta forma analizaremos diferentes tipos de datos, su funcionamiento y algunas de sus características.
Al necesitar que se ejecute en el navegador tendremos que tener en cuenta la compatibilidad entre versiones, por lo que nos hemos marcado como premisa que debe funcionar en cualquier navegador con un soporte mínimo de EcmaScript 5.1, es decir, desde Internet Explorer 9 en adelante. Las versiones anteriores podrían ser utilizadas por medio de algún polyfil, pero esto se escapa de nuestro alcance inicial. También debe funcionar en cualquier versión de Node.
Para empezar vamos a ver cómo tenemos que construir nuestro módulo para que pueda funcionar tanto en el navegador como en Node, ya que ambos sistemas son muy diferentes. Aunque existen soluciones como Browserify, y que la incorporación de los nuevos módulos de EcmaScript 6 abre nuevas posibilidades, vamos a tener una aproximación muy sencilla, evitando dependencias y que realmente funciona.
Aislar el código del módulo
El primer paso que vamos que dar para construir nuestro módulo es aislar las partes privadas de nuestro código. Más adelante veremos como publicar las funciones que nos interesan, ahora vamos a ver lo contrario, como proteger aquellos elementos que no tienen interés para otros.
Node
Una característica interesante que ofrece Node es que en los módulos las variables o funciones definidas dentro del fichero del módulo son locales al mismo y no se crean en el contexto global. Dicho de otra forma, si en nuestro fichero esequal.js
escribimos algo como:
var name = "esequal";
y en Node cargamos este fichero con
require("./esequal");
se habrá creado una variable con el nombre “name” en el contexto del módulo, pero el espacio global o del programa que hace uso del módulo no ha sido alterado. Por defecto el contenido de los módulos en Node es privado al módulo.
Navegador
En el navegador no tenemos un mecanismo similar y por defecto las variables o funciones definidas en un fichero .js
se crean en el contexto global. Por ello es muy facil que puedan producirse con facilidad conflictos y problemas con los elementos de otras librerías. Es decir, si cargamos ese fichero esqual.js
en el navegador con:
<script src="esequal.js"></script>
como resultado tendremos una variable global con el nombre name
.
Para evitar el problema en el navegador debemos ser capaces de controlar el contexto en el que se definen las variables y funciones locales a nuestro módulo y exponer en el contexto global sólo aquello que deseamos exportar. Parar conseguir este aislamiento debemos crear una clausura, que evitará que definamos variables o funciones globales, además de poder indicar que vamos a trabajar en modo estricto sin afectar a otros ficheros .js
que se definan con posterioridad a la carga de nuestro módulo. Por ello debemos crear una función de este tipo:
(function () { "use strict"; var name = "esequal"; })();
El código se ejecutará directamente, pero lo que definamos dentro de esta función queda definido sólo para este contexto, manteniéndose en contexto global sin ninguna alteración.
Exponer funciones
Ahora vamos a centrarnos en exponer la parte del código que sí nos interesa que sea utilizado fuera de nuestro módulo, es decir, vamos a ver como hacer público la función o funciones que nuestro módulo ofrece al resto de código.
Node
En Node debemos utilizar module.exports
para exponer una función de nuestro módulo.
"use strict"; function equal(a, b) { } module.exports = equal;
De esta forma la función equal
está disponible si cargamos el modulo con:
var esqual = require("./esequal");
Navegador
En el navegador vamos a exponer en el contexto global una función por medio de este conjunto de instrucciones:
(function (root) { "use strict"; function equal(a, b) { } root.equal = equal; })(this);
En este caso el this
que pasamos a la clausura corresponderá de forma habitual a window
, por lo que cuando escribimos root.equal
lo que estamos haciendo es definir una propiedad window.equal
. De esta forma se crea una variable global que apunta a la función que queremos exponer.
La solución general
Si unimos todo lo que hemos visto hasta ahora, obtendremos una función que puede función que puede funcionar como un módulo en el navegador y en Node. El resultado es un código similar a este:
(function (root) { "use strict"; function equal(a, b) { } // Export for node and browser if (typeof exports !== 'undefined') { if (typeof module !== 'undefined' && module.exports) { module.exports = equal; } exports.equal = equal; } else { root.equal = equal; } })(this);
Primera versión de esqueal.js
en GitHub
Como se puede observar, es un esquema bastante sencillo y que podemos utilizar con seguridad tanto en Node como en el navegador. Podremos utilizarlo con require("esequal.js")
o con <script src="esequal.js"></script>
sin ningún problema.
Conclusiones
Es cierto que existen otras soluciones para la gestión de módulos en el navegador, como Browserify, y que la incorporación de los nuevos módulos de ES6 abre una nueva vía de homogenización entre la programación en el cliente y en el servidor, pero una solución como la que hemos expuesto es realmente sencilla de implementar, la podemos utilizar sin necesidad de librerías externas en navegadores y funciona en versiones de modernas y antiguas de los navegadores y en Node sin necesidad de cambiar nada.
En los próximos artículo vamos a utilizar este esquema base para construir una función universal para comparar cualquier tipo de datos en Javascript. De momento ya hemos dado nuestro primer paso.
Novedades
HTTP2 para programadores. Enviar mensajes del servidor al cliente con Server Sent Event (sin WebSockets)
En esta charla, organizada por MadridJS, Pablo Almunia nos muestra cómo la mayoría de nosotros cuando oímos hablar por primera vez de HTTP2 nos ilusionamos con las posibilidades que presumiblemente se abrían para el desarrollo de soluciones web avanzadas y cómo muchos nos sentimos defraudados con lo que realmente se podía implementar.
En esta charla podemos ver cómo funciona el HTTP2, que debemos tener en cuenta en el servidor para hace uso de este protocolo y, sobre todo, cómo podemos enviar información desde el servidor al cliente de forma efectiva y fácil. Veremos con detenimiento cómo por medio de los Server-Sent Events (SSE) podemos recibir en el cliente datos enviados desde el servidor sin utilizar websocket, simplificando enormemente la construcción de aplicaciones con comunicación bidireccional.
Observables en Javascript con Proxies
En esta charla, organizada por MadridJS, Pablo Almunia nos habla de la observación reactiva de objetos en Javascript por medio de Proxy. Se describe paso a paso cómo funcionan los Proxies y en que casos pueden ser nuestro mejor aliado. Veremos que no hay que tenerles miedo, son bastante sencillos de utilizar, y nos ofrecen una gran abanico de posibilidades.
Aplicaciones JAMStack, SEO friendly y escalables con NextJS
En esta charla de Madrid JS, Rafael Ventura nos describe las funcionalidades clave de NextJS, nos muestra en vivo cómo desarrollar una completa aplicación JAMStack con Server Side Rendering (SSR) y Static Site Generation (SSG) y termina mostrando como publicar esta aplicación en Vercel.
Stencil JS: mejora el Time To Market de tu producto, por Rubén Aguilera
En esta charla Rubén Aguilera nos cuenta los problemas que tienen muchas empresas a la hora de sacar productos accesibles, vistosos y usables en el Time To Market que requiere Negocio y cómo podemos minimizar este tiempo gracias al DevUI con StencilJS para adecuar una aplicación de Angular a las exigencias del mercado en tiempo record.
breves
Javascript: 25 aniversario
25 años con Javascript han dado para mucho. Pero todavía queda mucho para este lenguaje de programación.
Descrubir algunas características de console
En el día a día nos encontramos muy a menudo utilizando console. Es una navaja multiusos que nos facilita la vida a la hora de depurar nuestro código. La mayoría de nosotros ha utilizado console.log(), pero tiene otras muchas funcionalidades.
Matrices dispersas o sparse arrays en JS
Una característica que puede producir algunos problemas, si no lo tenemos en cuenta, es la posibilidad de tener matrices con huecos, es decir, con algunos de sus elementos sin definir. Es lo que se suele denominar una matriz dispersa o sparse array. Veamos cómo trabajar con esta características de las matrices.
Operadores de bits usados con asiduidad
Cada día más a menudo podemos encontrar operadores binarios utilizados como formas abreviadas de algunas operaciones que de otra forma sería algo menos compactas y, quizás, más comprensibles. Veamos algunos casos en detalle.
Excelente, esta muy bien explicado. Es una solución bastante práctica y de gran utilidad ya que ahorra el tener que usar comandos y códigos externos para poder llegar a este mismo resultado. Muchas gracias. Saludos.
Muchas Gracias por el articulo, es muy interesante porfavor sigan sacando mas! :)….