Seleccionar página

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:

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:

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:

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

Explorando ArrayBuffer, DataView y matrices con tipo

Hasta hace relativamente poco en Javascript era complicado gestionar datos binarios. ArrayBuffer, DataView y las matrices con tipo (Typed Array) ponen a nuestra disposición un conjunto bastante completo de herramientas para manejar tipos binarios sin problemas. Vamos a ver cómo funcionan…

Objetos Map y Set

Los objetos Map y Set nos pueden ser de gran ayuda para gestionar conjuntos de datos, pudiendo simplificar nuestros programas en muchas circunstancias. Es interesante que sepamos cómo se utilizan y que pequeños secretos esconden. Vamos a revisarlos…

Referencia circular en objetos

Todos sabemos que los objetos pueden contener otros objetos, pero de lo que quizás no somos conscientes es que con mucha facilidad podemos crear una referencia circular, es decir, que si recorremos las propiedades del objeto y vamos profundizando, llegamos de nuevo al objeto inicial. Debemos tener en en cuenta esta circunstancia a la hora de realizar algunas operaciones o tendremos problema. Veamos cómo…

Características de las propiedades de los objetos

Existen varios tipos de propiedades que se comportan de forma diferente. Tenemos que tener en cuenta es la diferencia entre propiedades enumerables y no enumerables, propias y heredadas, de sólo lectura o no configurables, sin olvidar alguna que otra convención para definir propiedades como privadas. Veamos cómo trabajar con los distintos tipos de propiedades de un objeto.

breves

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.

Algunos 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.

Cómo diferenciar arrow function de function

En un reciente artículo Javier Vélez Reyes hace patente las principales diferencias entre las funciones tradicionales y las funciones flecha, ya que ambos modelos no son equivalentes e intercambiables. Veamos cómo es posible saber si una función ha sido construida por medio de la instrucción function o como una arrow function.