Cuándo (no) usar módulos o librerías externas en JavaScript

Cuándo no sabemos cómo programar la funcionalidad

Los módulos o librerías tienen que simplificar el proceso de desarrollo, no reemplazarlo. Su función es agilizar la escritura de código aportando una capa de abstracción sobre una funcionalidad cuyo desarrollo es complejo o engorroso. Si no sabemos cómo se hace algo, difícilmente podamos resolver los errores que surjan o sacar el máximo provecho a la herramienta que estamos usando.

Cuándo la funcionalidad aportada por el módulo o librería es mínima

Para entender esto nada mejor que referirnos a la historia de left-pad y como la baja por parte de su autor del módulo de 11 líneas de código provocó lo más parecido a un apocalipsis informático desde el Y2K. Casos similar es el módulo isarray con una línea de código y 10 millones de descargas semanales. Es necesario encontrar un balance, externalizar funciones muy sencillas nos expone a ciertos riesgos que no los corremos con módulos o librerías más complejas; no hacerlo implica tener que escribir más código con lo cual podemos cometer más errores pero nos aseguramos que esté siempre disponible y sea funcional en nuestro ecosistema.

Cuándo vamos a utilizar una única función de una librería multipropósito o general

Es muy común ver en una página, Bootstrap y sus dependencias jQuery, Hammer.js y Popper.js al completo para utilizar una grilla para acomodar los contenidos o jQuery, jQuery UI, y Select2 para implementar un select con autocomplete o, más últimamente, React + Babel para mostrar un sitio a todos los efectos estático del lado del cliente. Si únicamente vamos a necesitar una funcionalidad específica es más que probable que existan algunas librerías específicas o que su implementación sea lo suficientemente sencilla para que podamos crear nuestro propio módulo.

Cuándo necesitamos la máxima velocidad y el mínimo tamaño

Raramente es necesario lograr tan alto nivel de optimización pero, de hacer falta, siempre se logran mejores resultados mientras menos capas de abstracción y recursos externos utilicemos.

Propiedades no enumerables en JavaScript

Hasta la especificación ES5 de JavaScript las propiedades de un objeto consistían únicamente de un nombre y un valor. A partir de 2009, fecha de publicación, se agregó la posibilidad de establecer 3 atributos de las propiedades que permiten controlar sus características:

  • configurable
  • writable
  • enumerable

Configurable

Permite establecer si, luego de su creación, se podrán editar los atributos, eliminar la propiedad o modificar las funciones de acceso. Hay que tener en cuenta que una vez establecido a false, no puede volver ser modificado.

Writable

Indica si se puede modificar el valor de la propiedad mediante el uso de operadores de asignación.

Enumerable

Configura si la propiedad va a aparecer en el listado de propiedades del objeto. Por ejemplo, mediante el uso de Object.keys(). Si bien, en principio, no parece tener mucha utilidad es importante considerar qué otras funciones utilizan la enumeración de propiedades para ver su potencial.

Ya mencioné con anterioridad que el operador de propagación y Object.assign() únicamente copian aquellas propiedades enumerables. Además, permite ocultar propiedades de los bucles for … in, ya que al no ser enumeradas no son recorridas por el mismo.

Pero, una de las consecuencias más útiles de establecer una propiedad como no enumerable es que serán ocultadas a JSON.stringify(). Esto nos permite tener, de forma sencilla, total control sobre la información que exponemos del objeto.

var obj = {
    'prop1': 'value1'
};
Object.defineProperty(obj, 'prop2', { value: 'value2', enumerable: false });

JSON.stringify(obj); // { prop1: value1 }

Limitaciones y diferencias de Object.assign() y el operador de propagación en JavaScript

Con el (nuevo) auge de la programación funcional y de librerías como Redux ha resurgido el concepto de inmutabilidad. La existencia de estructuras de datos que no pueden cambiar su valor luego de haber sido iniciadas. Cualquier manipulación de la información requiere que creemos un nuevo objeto que copie los datos del original e introduzca los cambios deseados.

Más allá de las ventajas y desventajas de la inmutabilidad, en la práctica, cuando lo implementamos en nuestras aplicaciones nos encontramos en la necesidad de copiar con cierta asiduidad estructuras de datos. En ES6, como siempre en JavaScript, la forma elegida para la representación de estos datos son los objetos. Y aquí llegamos al tema del post, existen varias formas de copiar objetos.

Dejando de lado la forma más obvia y chabacana de copiar «manualmente» propiedad por propiedad, existen dos métodos para obtener una copia modificada de un objeto:

Object.assign()

obj = Object.assign( target, source1, ..., sourceN )

Como su nombre lo indica, esta función no está pensada originalmente para clonar objetos sino para asignar nueva información a un objeto existente pero pasando como target un objeto «vacio» podemos lograr el mismo efecto.

Operador de propagación

obj = { ...source1, ..., ...sourceN }

Ídem que con el caso anterior, la idea original de el operador de propagación no era copiar objetos sino simplificar la expansión de expresiones en situaciones donde se esperaba una cantidad múltiple y, generalmente, desconocida de parámetros. En este caso, la creación de un nuevo objeto que reciba como contenido el objeto original expandido es el truco para lograr  la copia deseada.

Diferencias

La diferencia entre estos métodos es que el primero setea nuevos valores en las propiedades de un objeto mientras que, el segundo, crea nuevas propiedades y les asigna información.

Es decir, si tenemos setter definidos en nuestro objeto, Object.Assign() los va a llamar y el operador de propagación, no. Esto también tiene como consecuencia que si nuestro objeto tiene propiedades de solo lectura el operador de propagación no las va a respetar, únicamente el Object.Assign().

Limitaciones

Las principales limitaciones que nos encontramos con ambos métodos es que la copia realizada es superficial y que únicamente se consideran las propiedades propias y enumerables del objeto.

Es decir, el nuevo objeto no ninguna propiedad definida en prototype, heredada o que se haya indicado explícitamente como no enumerable. Además, si tenemos algún objeto anidado este no se copiará sino que lo hará su referencia.

 

En conclusión ambas opciones son muy similares y su rendimiento es casi idéntico, aunque un poco mejor el del operador de propagación, por lo que el decantarse por uno u otro depende más de cuestiones subjetivas.

Particularmente, encuentro el funcionamiento del operador de propagación más acorde a lo que significa trabajar con objetos inmutables ya que si o si crea un nuevo objeto. Además, al momento de tratar con objetos anidados, resulta mucho más legible:

cons obj = {
    prop1: value1,
    obj1: {
        prop2: value2,
        prop3: value3
    }
}

const copia = {
    ...obj
    obj1: {
        ...obj.obj1
    }
};

Rendimiento de WSL

Una de las grandes features que trajo la Anniversary Update de Windows 10 es el Windows Subsystem for Linux (WSL), una compatibility layer que permite correr de forma nativa los ejecutables ELF de Linux. Si bien no está pensado para correr un X Window (aunque hay por ahí circulando guías para poder lograrlo) puede perfectamente ejecutar todas esas herramientas y servicios hoy completamente intrínsecos al desarrollo web como son los package managers, linters, test suites, étc.

Como es de esperarse cuando «virtualizamos» un SO tenemos una caída del rendimiento pero, en principio, los tiempos del WSL dejan mucho que desear. Tareas simples como actualizar un paquete con npm o procesar algunos archivos SASS puede demorar varias veces más de lo habitual, incluso varios minutos.

Con un rápido monitoreo se ve que cuando realizamos tareas en WSL se dispara el uso del procesador y del disco. Uno de los procesos que más sube su uso de los recursos del sistema es el asociado al Antimalware / Defender.  Indagando un poco en Google podemos ver que, como sucede desde la época de Vista, Windows sigue sin llevarse bien con los directorios con muchos archivos pequeños.

La filosofía de Linux de tener cientos de módulos con responsabilidades mínimas sumado al escaneo por parte de Windows de cada uno de estos archivos cuando es accedido o modificado hace que los tiempos se multipliquen.

Por lo visto, de momento la única solución posible es desactivar la protección en tiempo real de Windows Defender. La exclusión de la carpeta de datos y del ejecutable de WSL parece no alcanzar por si sola para mejorar la performance aunque probablemente tenga que ver más con que, al ejecutarse de forma nativa los programas de Linux, se nos estén pasando agregar algunos.

Una vez detenido la protección en tiempo real la performance general mejora drásticamente aunque sin llegar a ser la misma que observamos en Linux. Desde el equipo detrás de WSL prometen estar haciendo lo posible para mejorar estos tiempos pero como el problema es intrínseco al sistema de archivos no es probable que se vayan a ver soluciones definitivas en el corto plazo.

Usar repositorios Git como paquetes de Composer

No todos los proyectos de GitHub están registrados en packagist.com o tienen creado un «composer.json» que nos permita agregarlo de forma sencilla en nuestro proyecto. Por suerte, composer nos permite salvar esta situación cargando los datos del repositorio directamente en nuestro archivo de dependencias.

Para hacerlo debemos cargar la información del proyecto en el array «repositories», especificando dónde están los fuentes, el nombre, la versión, étc.

{
  "repositories": [
    {
      "type": "package",
      "package": {
        "name": "fullcalendar/fullcalendar",
        "version": "2.5.0",
        "dist": {
          "url": "https://github.com/fullcalendar/fullcalendar/archive/v2.5.0.zip",
          "type": "zip"
        },
        "source": {
          "url": "https://github.com/fullcalendar/fullcalendar.git",
          "type": "git",
          "reference": "tags/v2.5.0"
        },
        "require": {
          "components/jquery": ">=1.7.1",
          "moment/moment": ">=2.5.0"
        }
      }
    },
  ]

Como vemos, en última instancia, lo que hacemos es indicar manualmente toda la información que debiera tener el proyecto si estuviera cargado en el repositorio de packagist.com. Una vez hecho esto podemos usar el paquete como cualquier otro dentro de este proyecto con el nombre indicado en la propiedad «name».

Por ejemplo, el archivo «composer.json» de un proyecto donde incluyamos la librería Fullcalendar Scheduler, que depende de esta, quedaría así:

{
  "name": "rvaccaro/agenda",
  "authors": [
    {
      "name": "Roberto Vaccaro",
      "email": "xxxx@xxxx.com"
    }
  ],
  "repositories": [
    {
      "type": "package",
      "package": {
        "name": "fullcalendar/fullcalendar",
        "version": "2.5.0",
        "dist": {
          "url": "https://github.com/fullcalendar/fullcalendar/archive/v2.5.0.zip",
          "type": "zip"
        },
        "source": {
          "url": "https://github.com/fullcalendar/fullcalendar.git",
          "type": "git",
          "reference": "tags/v2.5.0"
        },
        "require": {
          "components/jquery": ">=1.7.1",
          "moment/moment": ">=2.5.0"
        }
      }
    },
    {
      "type": "package",
      "package": {
        "name": "fullcalendar/scheduler",
        "version": "1.1.0",
        "dist": {
          "url": "https://github.com/fullcalendar/fullcalendar-scheduler/archive/v1.1.0.zip",
          "type": "zip"
        },
        "source": {
          "url": "https://github.com/fullcalendar/fullcalendar-scheduler.git",
          "type": "git",
          "reference": "tags/v1.1.0"
        },
        "require": {
          "components/jquery": ">=1.8.0",
          "moment/moment": ">=2.5.0",
          "fullcalendar/fullcalendar": ">=2.5.0"
        }
      }
    }
  ],
  "require": {
    "fullcalendar/scheduler": "1.1.*",
  }
}

Usar Git+PuTTY para conectarse a repositorios en Windows

Instalar GIT para Windows – Descarga
Instalar PuTTY – Descarga 

Utilizar PuTTYgen para crear un par de claves
Abrir PuTTYgen, cambiar la cantidad de bits y el tipo de clave si hiciera falta. Hacer click en «Generate». Mover el mouse en el cuadrado vacio hasta que la barra de progreso se llene. Una vez que esté generada la clave se habilitan los botones «Save public key» y «Save private key» que nos permiten guardar, respectivamente, la clave pública y la privada. El nombre se puede elegir libremente, conviene utilizar el mismo. La clave pública no tiene extensión predeterminada pero es práctico utilizar txt para acceder más rápidamente.

Agregar la clave privada a Pageant

Agregar la clave pública al servidor GIT

Indicar que PuTTY es tu almacén de claves SSH
…asignando la variable de sistema o usuario GIT_SSH al ruta de «plink.exe», ubicado en la misma carpeta de PuTTY. En Windows 7, entrar al Panel de Control, Sistema, Configuración avanzada del sistema, Variables de Entorno, en Variables de usuario o del sistema hacer click en Nueva. Usar como nombre GIT_SSH y como valor la ruta completa a plink, por ejemplo: «C:\Program Files (x86)\PuTTY\plink.exe»