Cómo empezar bien sus proyectos

Elija su entorno de desarrollo

1. Introducción

JavaScript ha carecido durante mucho tiempo de un IDE (entorno de desarrollo integrado). La mayoría de las veces, los desarrolladores se conformaban con editar el código con un simple editor de texto, centrándose en los asistentes de páginas HTML/CSS y olvidándose de JavaScript. La evolución de este lenguaje ha hecho que pasemos de un uso más bien marginal a uno masivo, especialmente con la llegada de los dispositivos móviles y HTML 5. Muchos editores de código se han adaptado y ahora ofrecen entornos específicos para JavaScript. Por supuesto, siempre es posible utilizar un editor de texto generalista (como Notepad++, vi, Emacs, etc.) y trabajar con él, pero a menudo esto supone privarse de valiosos asistentes específicos para JavaScript que ofrecen un ahorro real de tiempo en su trabajo.

He aquí algunos aspectos imprescindibles que debe gestionar un editor de código JavaScript:

  • Gestión de la codificación de caracteres (UTF-8, ISO-8859-1, etc.).

  • Resaltado de sintaxis.

  • Verificación sintáctica en tiempo real.

  • Asistentes de escritura.

  • Refactorización (simplificación del mantenimiento del código).

  • Formateo del código.

  • Gestión de proyectos.

Al crear un archivo JavaScript, es preferible utilizar una codificación portátil del tipo UTF-8. Este tipo de codificación Unicode puede contener una secuencia de caracteres al principio del archivo denominada BOM (Byte Order Mark), que sirve para indicar el orden de los bytes, pero también para identificar la codificación Unicode UTF-8, UTF-16 o UTF-32. Es preferible no utilizar esta secuencia, ya que no todos los editores o herramientas la admiten correctamente.

El resaltado sintáctico y la verificación sintáctica están relacionados. El ahorro de tiempo puede ser considerable debido al uso frecuente de paréntesis y llaves en JavaScript. Las anidaciones de funciones y objetos pueden hacer que la sintaxis resulte menos legible. El hecho de que salte una alerta en el momento de escribir supone un valioso ahorro de tiempo que le evitará tener que esperar a la fase de ejecución y a la interpretación de un mensaje de error en una consola (navegador o línea de comandos).

Un asistente de escritura puede...

Organizar el código

1. Separar el código de la presentación

En un contexto web, resulta más legible evitar que el código de presentación y el de procesamiento de JavaScript se mezclen. El objetivo es facilitar el mantenimiento de cada uno de ellos, reduciendo los efectos secundarios (modificar una parte alterando la otra).

He aquí un ejemplo poco recomendable que, sin embargo, muestra correctamente la fecha al cargar la página.

<!DOCTYPE html>  
<html>  
    <head>  
        <title>Prueba</title>  
    </head>  
    <body>  
        <h1>Hola</h1>  
        <script>  
            document.writeln( "Hoy es " + new Date() );  
        </script>  
    </body>  
</html> 

El código JavaScript se encuentra aquí dentro de la etiqueta body, lo cual no suele ser una buena idea. El uso de document.writeln debe evitarse en la medida de lo posible, ya que solo se ejecuta en el momento en que el navegador interpreta la etiqueta script, lo que obliga a colocar el código en un punto concreto y provoca una mezcla inevitable entre el código de presentación, escrito en HTML, y el código de procesamiento, escrito en JavaScript.

Por lo tanto, este código resulta muy difícil de mantener. Para mejorarlo, lo primero que se puede hacer es subir el script a la etiqueta head de la página. Por supuesto, al hacerlo, ya no podemos utilizar writeln (este método escribe en el flujo de la página que se está cargando y la etiqueta head no contiene información visible para el usuario final). Por ello, es necesario actualizar el contenido de la página una vez que se haya cargado por completo y determinar la zona que contendrá el resultado del código. La etiqueta body se convierte entonces en una especie de plantilla de página. El uso de la API DOM (Document Object Model) permite añadir ese resultado (véase el capítulo Mejorar sus habilidades web). El atributo...

Ámbito de las variables y funciones

1. Ámbito de las variables

Cuando se declara una variable, esta solo es «visible» en determinados lugares. Cuando la variable se declara fuera de cualquier función, su ámbito se denomina global, por lo que es accesible en cualquier parte del código.

a = 10;  
function test() {  
  alert( a );  
}  
test(); 

En el ejemplo, hemos inicializado una variable a con el valor 10. Se declara implícitamente en el momento de su uso. Al estar fuera de cualquier función, su ámbito es global.

Supongamos ahora que escribimos:

function test() {  
  a = 10;  
}  
test();  
alert( a ); 

Ahora hemos inicializado la variable a en la función test. Como tenemos una declaración implícita, habríamos esperado que a ya no fuera global. Sin embargo, ocurre exactamente lo contrario: la variable a declarada en la función test es global por defecto y podemos mostrar su valor fuera de la función test.

Esto no es aceptable, ya que podríamos alterar fácilmente la ejecución de nuestras funciones si utilizamos el mismo nombre de variable.

Para evitar este tipo de inconvenientes, disponemos de la palabra clave let. Esta servirá para declarar la variable, limitando su uso a un contexto concreto. Así, cuando se utiliza dentro de una función, el ámbito...

Optimización del ámbito

1. Limitar el contexto global

Las declaraciones en el contexto global son perjudiciales en términos de calidad (y, por lo tanto, de mantenimiento del código), pero también en términos de rendimiento.

En cuanto a la calidad, cuantas más declaraciones haya en el espacio de nombres global, mayor será la probabilidad de que se produzca un conflicto al modificar una variable por error. Este tipo de conflicto puede surgir por diversos motivos: porque el código ha crecido tanto que ya no se pueden controlar todas las declaraciones, o porque se añaden nuevas bibliotecas cuyo contenido no se domina. Si su código va a evolucionar, es importante tener en cuenta este problema en el momento del diseño.

En cuanto al rendimiento, leer una variable local es aproximadamente dos veces más rápido que hacerlo con una variable global, ya que el intérprete de JavaScript siempre empieza por comprobar el contexto local antes de mirar el contexto global. Por lo tanto, leer una variable global supone una comprobación adicional para el intérprete.

En el siguiente ejemplo, hemos creado dos bucles que realizan un cálculo sencillo; uno utilizando el contexto global, y el otro, el contexto local. A continuación, hemos medido la diferencia de ejecución en milisegundos.

let i = 0;  
let j = 0;  
// Versión global  
   let test1 = function() {  
      for ( i = 0; i < 1000000; i++ ) {  
        j += j;  
      }  
   }  
 
// Versión local  
   let test2 = function() {  
      let j = 0;  
      for ( let i = 0; i < 1000000; i++ ) {  
        j += j;  
    }  
}  
 
let tests = [ test1, test2 ];  
 
for ( let n = 0; n < tests.length; n++ ) {  
    let init = new Date().getMilliseconds();  
    tests[ n ]();  
    alert( "El test " + n + " ha tardado " + ( new  
Date().getMilliseconds() - init ) + " ms"...

Simplificar sus expresiones

1. El operador ||

Ya conoce el operador booleano ||. Este operador realiza un «o lógico», pero también tiene la particularidad de devolver el primer valor evaluado como verdadero (true). En JavaScript, un valor se considera implícitamente verdadero si es diferente de false, undefined, 0, null y "", y se considera falso en el caso contrario.

Ejemplo

let a = null;  
let b = a || 10;  
alert( b ); 

En nuestro ejemplo, hemos inicializado la variable a como null (por lo que se considera implícitamente «falso»), así que la variable b recibe el único valor que es verdadero gracias al operador ||, es decir, el valor 10.

El operador || puede utilizarse, por tanto, para garantizar un valor de forma práctica.

function test( a ) {  
  a = a || 10;  
  alert( a );  
}  
test();  
test( 11 ); 

En este ejemplo, tenemos un parámetro de función a que puede pasarse o no; si no se pasa, obtendrá automáticamente el valor 10 por defecto. Por lo tanto, se mostrará 10 en una llamada sin parámetro y 11 en una llamada con el parámetro.

También es posible tener un parámetro que contenga un conjunto de propiedades presentes solo en parte.

function action(config) {  
    let defConfig = {  
        color: "blue",  
        fuente: "arial"  
    };  
    config = config || defConfig;  
    let color = config.color || defConfig.color;  
    let fuente = config.fuente || defConfig.fuente;  
    alert( color + " y " + fuente );  
}  
 
action();  
action( { color: "red" } );  
action( { color: "red", fuente: "times" } ); 

En la función action, tenemos un parámetro opcional config, que contiene, en parte o en su totalidad, las propiedades color y fuente. Gracias al operador ||, siempre tenemos un valor para cada propiedad, recurriendo, si es necesario, a un objeto predeterminado (defConfig).

El operador || es sin duda práctico, pero debe utilizarse con prudencia.

He aquí...

Paso al modo estricto

Uso

El modo estricto es una novedad del estándar ECMAScript 5 (compatible con Chrome/Edge/Firefox); se trata de una forma de mejorar la calidad de su código a un coste reducido. También está pensado para facilitarle la transición a las próximas versiones de JavaScript.

Este modo se activa al principio de su código o de sus funciones mediante la siguiente cadena:

"use strict"; 

Con ella se indica a su navegador que ya no se permiten ciertos tipos de código y que debe lanzarse un error de sintaxis cuando se detecten. Cabe señalar que colocar esta cadena al principio de su código o de sus funciones también es perfectamente compatible con los navegadores que no admiten este modo (una cadena sola es una expresión que no tiene ningún efecto).

Ejemplo de una función en modo estricto:

<!DOCTYPE html>  
 
<html>  
    <head>  
        <script>  
            "use strict";  
 
            function test() {  
                aa = 10;  
            }  
 ...

Analizadores de código

1. JSHint

JSHint (http://www.jshint.com) es una biblioteca de JavaScript basada en JSLint (un proyecto similar). Su objetivo es analizar su código y avisarle si se pueden realizar mejoras. No se trata necesariamente de errores en sí, sino más bien de un control de calidad del código, como por ejemplo la sangría, el paso sistemático al modo estricto…

Básicamente, JSHint funciona mediante una función global JSHINT que recibe tres argumentos:

  • Su código fuente JavaScript o un directorio para analizarlo todo.

  • Un conjunto de opciones que indican a JSHint qué debe comprobar en el código, como la verificación de bloques vacíos, la sangría, la convención de nomenclatura…

  • Un conjunto de variables globales no obligatorias con un «flag» (booleano) para indicar si pueden modificarse.

Si el código que pasa es correcto, la función devuelve «true»; de lo contrario, devuelve «false».

Para instalar JSHint:

npm install -g jshint 

Si, por ejemplo, tenemos un directorio CH1 que contiene todos nuestros scripts y un directorio node_modules que contiene nuestras bibliotecas externas, nos situaremos, desde la línea de comandos, en el directorio padre de CH1 y luego utilizaremos esta instrucción:

jshint CH1 --exclude .\CH1\node_modules 

Por defecto, JSHint genera advertencias en cuanto...

Documentar su código con JSDoc

1. Principio general

JSDoc parte del concepto de JavaDoc en Java y de las anotaciones. Consiste en integrar marcadores en los comentarios. Gracias a estos marcadores, es posible generar una documentación más legible al tiempo que se refuerzan ciertos controles. Algunas herramientas adicionales pueden comprender mejor su código para realizar procesos de compresión con Google’s Closure. Por último, algunos editores como VSCode o WebStorm son capaces de interpretar sus marcadores y asistirle mejor durante la escritura.

También se puede decir que JSDoc es, en cierto modo, una extensión de JavaScript en términos de tipado para identificar mejor los valores empleados.

Para utilizar JSDoc, sus comentarios deben tener el siguiente formato:

/** Sus comentarios  
*/ 

Tenga cuidado de no olvidar el doble asterisco al principio del comentario.

Un marcador comienza por @; por ejemplo, @param designará un parámetro de función.

2. Los marcadores

a. Declaraciones

El caso más sencillo es el uso del marcador @type para indicar la naturaleza de una variable o constante:

// @ts-check  
 
/** @type {number} */  
let a = 10;  
/** @type {string} */  
let miCadena = "hello world";  
/** @type {boolean} */  
let estado = true;  
 
/** @type {number} */  
const aa = 10; 

En el ejemplo anterior, comprobamos que los tipos primitivos (en minúsculas) number (número), string (cadena) y boolean (booleano) se corresponden con nuestros valores.

b. Funciones

Para documentar una función, normalmente utilizará @param y @return:

// @ts-check  
 
/**  
*    Realizar una operación binaria  
*    @param {number} operando1 Un entero  
*    @param {number} operando2 Un entero  
*    @param {string} operador Una cadena que designa...

Paso de un entorno de desarrollo a un entorno de producción

1. Pruebas unitarias

a. Introducción

Cuando su código es estable y tiene funciones o clases críticas, puede ser conveniente implementar pruebas unitarias para validar su funcionamiento y limitar el problema de las «regresiones» tras las actualizaciones.

Desde un punto de vista práctico, resulta mucho más fácil implementar pruebas unitarias si las funciones o clases que se van a probar se encuentran en archivos JavaScript independientes de sus páginas HTML.

La forma más sencilla de realizar pruebas unitarias consiste en evaluar un conjunto de expresiones y comprobar que el resultado coincide con un resultado determinado. Si no es así, la prueba unitaria falla y el paso a producción debe posponerse hasta que se resuelva el problema.

b. Gestión con su propia biblioteca

Implementar su propio sistema de pruebas unitarias no tiene por qué ser muy complejo. Depende, ante todo, de cómo se haya estructurado su código. Por ejemplo, puede crear tantas páginas HTML de prueba como scripts tenga.

Tomemos un ejemplo sencillo con un conjunto de funciones de calculo. Supongamos que estas funciones se encuentran en un archivo independiente calculo.js con este tipo de estructura:

let calculo = ( function() {  
 
    let calculo = {};  
 
    /**   
     * @param a {number} 
     * @param b {number} 
     * @return {number}  
     */  
    function division( a, b ) {  
          return a / b;  
    }  
 
    /**   
     * @param a {number}  
     * @param b {number} 
     * @return {number}  
     */      
    function suma( a, b ) {  
          return a + b;  
    }  
 
    calculo.division = division;  
    calculo.suma...

Apoyarse en una inteligencia artificial (IA)

1. Principios

Los principales LLM (Large Language Models), como ChatGPT, han sido entrenados con enormes cantidades de código informático, lo que puede agilizar la corrección de código e incluso la generación de ciertos bloques de código. En teoría, es posible crear aplicaciones completas con JavaScript, pero bajo su propia responsabilidad, ya que un LLM tiene sobre todo un enfoque estadístico y, en caso de error o problema, no siempre le será de gran ayuda.

2. Gemini CLI

Gemini CLI (https://github.com/google-gemini/gemini-cli) es un agente de IA de código abierto basado en Gemini, la IA de Google. Dispone de un asistente de línea de comandos para ayudarle a gestionar su código (JavaScript, HTML, CSS…).

Podemos instalar este agente mediante npm con:

npm install -g @google/gemini-cli 

Una vez instalado, puede consultar a su agente de IA mediante el comando gemini, desde su directorio de trabajo. La primera vez que lo inicie, el asistente le ofrecerá elegir su método de autenticación: podrá conectarse directamente a su cuenta personal de Google (lo que le otorgará un uso gratuito de hasta 1000 consultas al día) o, alternativamente, asociar una clave de API obtenida en https://aistudio.google.com/apikey configurándola en la variable de entorno GEMINI_API_KEY.

Una vez ejecutado gemini, solo tiene que pedirle al agente que le explique un tema determinado mediante un prompt. El agente tiene acceso a sus archivos, por ejemplo, situándose en un directorio modules que contenga el archivo calculo.js.

Es posible pedirle a la IA una explicación sobre la función de este archivo; por ejemplo:

images/icetoile.png El archivo calculo.js es un módulo JavaScript que proporciona  
funciones de cálculo básicas.   
   
  En resumen, sirve para:   
   
   1. Definir operaciones matemáticas: contiene una función “suma” 
   para la suma y una función “división” para la división.   
   2. Gestionar los errores: la función “division” incluye una 
   medida de seguridad que impide la división por cero generando 
   un error, lo que se considera una buena práctica....