Javascript

Javascript es el lenguaje de programación (a modo de “script”) creado originalmente para interactúar con páginas HTML (accediendo al DOM - Document Object Model), siendo utilizado frecuentemente para el desarrollo Web en el navegador. Con el entorno de programación Node.js este lenguaje opera también en el servidor. Con la tendencia de la Web moderna en 2015 se liberó actualización significativa del lenguaje (sobretodo en su estándar para el manejo de clases y promesas con flujos asíncronos) conociéndose como es6 o ES2015 y así ha continuado con nuevas actualizaciones (es7, es8, es9…).

Esta es una referencia ágil para quién tenga nociones de programación o codifique con algún otro lenguaje, también a modo de repaso y uso frecuente. Siendo así, tener esta información como memoria te resultará simple de acceder a los fundamentos, incluso como prueba de concepto sobre el lenguaje.

Ejemplo esencial

function hi() {
    console.log('Hi there!')
}

Un proyecto de inicio rápido se encuentra finalizando el documento

Tips esenciales del lenguaje

Para quien tenga habilidades, experticia en programación y/o requiera agilidad en conceptos técnicos, pueden resumirse los siguientes tips esenciales del lenguaje:

  1. Tipos de datos básicos: number, float, boolean, string, array, object. Las variables se definen simplemente con let (o var) y la asignación del valor. La función typeof puede identificar el tipo de datos de una variable en un momento dado.
  2. Las funciones se definen con function, luego los parámetros van entre paréntesis (...) (sin especificar el tipo). Los parámetros se separan con coma ,. Además, se usa return para retornar un valor. También existe una alternativa moderna de expresar funciones (llamada funciones de flecha) que omite la palabra function y en su lugar se usa: () => {...}.
  3. Para el bloque de la función o el flujo de control se usan llaves {}. Actualmente, las sentencias pueden terminar sin punto y coma ;, siendo solamente necesario para separar y admitir varias sentencias en la misma línea (por lo demás, ya no se necesita).
  4. El flujo de control es semejante a lenguajes como C, Java, Kotlin, Dart, es decir que se cuenta con una anatomía cercana para el uso de if, for, while, incluso para el manejo de excepciones (try).
  5. El constructor de una clase lleva el nombre constructor y la clase se define con la palabra reservada class y el nombre.
  6. Actualmente, es posible usar operador de nulo seguro (null-safe): ?..
  7. A diferencia de otros lenguajes, el uso de JSON es nativo del lenguaje (siendo básicamente la mezcla entre array y object). Para esto se cuenta con las funciones JSON.stringify y JSON.parse.

Tipos de datos básicos

Javascript no es un lenguaje tipado, sin embargo, se pueden citar los tipos de datos primitivos y complejos

Tipo Descripción
string Cadena de caracteres o texto
number Tipo de datos primitivo para números
float Numero flotante
boolean Booleano
undefined Indefinido (sin tipo determinado)
null nulo
array Arreglo, se representa como corchetes []
object Objeto, se representa como llaves {}
function función, se representa como paréntesis () o function
Date Objeto para fechas

Se podría decir que la combinación de objetos y arreglos en Javascript es lo que se conoce como JSON, notación usada frecuentemente en interfaces de programación de aplicaciones (API), incluso por otros lenguajes como formato de intercambio de datos.
Adicionalmente, se puede identificar el tipo de datos de una variable con la función typeof(x), dónde x sería la variable a evaluar.

Declaración de variables

Se pueden declarar variables con las palabras reservadas var, let y const, esta última para indicar que el valor no cambia o no es mutable. let sustituye a var siendo introducido para evitar riesgos por reasignacion de variables previamente definidas conforme al ámbito del programa.

Ejemplo:

var variable = 'Ana'
let variable2 = []
const immutable = "Ana"

var, let y const varían en el ámbito. var puede ser usada para alcance global o de una función, no respeta bloques y pueden delacarse después de su inicializacion (hoisting). let respeta los ámbitos partiendo del bloque. const también tiene ámbito de bloque para constantes (no puede tener reasignación).

Definición de funciones

function plus1 (a, b) {
    return a + b
}

const plus2 = (a, b) => { a + b }

Comentarios

// Esto es un comentario de fin de línea

/*
   Este es un comentario de bloque
*/

Condicionamiento if / else (if)

if (i == 1) {
    console.log('one')
}
else if (i == 2) {
    console.log('two')
}
else {
    console.log('aha')
};

El Ciclo For

for (var index = 0; index < list.length; index++) {
    console.log(index)
}
for (var item in list) {
    console.log(item)
}

Puede interrumpirse un ciclo con la sentencia break o dar el paso a la siguiente iteración con la setencia continue.

El Ciclo While

while (i < 10) {
    i++
}
do {
    i++
}
while (i < 10)

Puede interrumpirse un ciclo con la sentencia break

Evaluador Switch

switch (i) {
    case 1: console.log("one"); break;
    case 2: console.log("two"); break;
    default: console.log("aha")
}

Iteracion forEach

list.forEach(function (item) {
    console.log(item)
})

Excepciones

try {
    ...
}
catch (e) {
    ...
}
finally {
    ...
}

Puedes usar throw (por ejemplo: throw new Error('error!')) para forzar el lanzamiento de un error.

Clases

La programación orientada a objetos (OOP) tiene su propia base de conceptos que abre un capítulo aparte para su comprensión, se puede decir que se pasa de pensar en funciones a clases que agrupan métodos (funciones) y propiedades (variables), siendo de gran útilidad para la reutilización de código. Como referencia simplemente se presenta la anatomía de una clase.

class Circle extends Shape {
    constructor (radius) {
        this.radius = radius
    }

    expand (n) {
        return super.expand(n) * Math.PI
    }
}

Módulos

Pensar en módulos es organizar el código de modo que se pueden usar archivos que hacen las veces de librerías, por lo que se exponen o exportan funciones o variables y se importan en el script que los utiliza.

import Helper from 'helper'
export default function () { ··· }
export function mymethod () { ··· }

Promesas

Las promesas son la respuesta de acogida de un estándar en el flujo asíncrono del lenguaje (antiguamente se anidaba demasiado el código o se recurría al uso de la librería JQuery). Al definir promesas se pasa una función con dos parámetros (resolve, reject) que permitirán indicar respectivamente si se resuelve o rechaza, de allí que se consideran ciertos estados.

Estado Descripción
Pendiente Estado inicial
Resuelta Cuando se ha resuelto (con resolve)
Rechazada Cuando ha sido indicado su rechazo (con reject), lo cual genera el flujo de error (catch)
new Promise((resolve, reject) => {
  if (ok) { resolve(result) }
  else { reject(error) }
})
promise
    .then((result) => { ··· })
    .catch((error) => { ··· })
    .finally(() => { ... })
Promise.all(···)
Promise.race(···)
Promise.reject(···)
Promise.resolve(···)
async function run () {
  const user = await getUser();
  return user;
}

Funciones esenciales incorporadas

Función Descripción
parseInt(x) Convierte una cadena de texto que corresponde a un número en un entero.
parseFloat(x) Recibe una cadena de texto que corresponde a un número flotante y lo convierte en flotante.
isNaN(x) Cuando parseInt o parseFloat no resuelven la conversión devuelven el valor NaN. Con esta función se determina si fué incorrecta la conversión o no (true/false).
typeof Determina el tipo de datos de una variable (undefined, string, number, function, object)
x.toString() Convierte un número en una cadena, dónde x es una variable que corresponde al número. También puede encontrarse en objetos.
x.toUpperCase() Covierte una cadena a mayúsculas. Es un método de objetos String por lo que se usa como x.toUpperCase(), dónde x es una variable que contiene una cadena de texto.
x.toLowerCase() Covierte una cadena a minúsculas. Es un método de objetos String por lo que se usa como x.toLowerCase(), dónde x es una variable que contiene una cadena de texto.
x.replace('a','b') Reemplaza la primera coincidencia en una cadena con una nueva. Es un método de objetos String por lo que se usa como x.replace('a','b'), dónde x es una variable que contiene una cadena de texto. Dado que no existe algo como replaceAll se pueden usar expresiones regulares (regex, por ejemplo: x.replace(/a/g,'b')).
x.substring(i,j) Devuelve una cadena recortada entre las posiciones i y j - 1. Si no se especifica el segundo parámetro se devuelve el resto de la cadena a partir de la posición inicial indicada.
x.split(s) Convierte una cadena en un arreglo dado un separador (por ejemplo, ,)
x.indexOf(sub) Devuelve el índice o posición de la primera coincidencia de una subcadena. Si no existe retorna -1, mientras 0 indicaría que se encuentra en la posición inicial. Enviando un segundo parámetro se puede indicar que inicie la evaluación desde una posición específica.
JSON.stringify(o) Convierte un objeto en una cadena de texto estilizado como JSON, mas el resultado ya no es un objeto JSON sino una cadena.
JSON.parse(s) Convierte una cadena que contiene texto como JSON a un objeto Javascript.

Funciones y propiedades sobre arreglos

Función Descripción
x.length Propiedad que reporta el número de elementos del arreglo (también aplica para longitud de cadenas)
x[i] Expresión para devolver el elemento indicado del arreglo. Si es un objeto en lugar de un vector (o lista) entonces podría agregarse el elemento del objeto (ej. x[i].a).
x.push(e) Agrega un elemento al final del arreglo
x.unshift(e) Agrega un elemento al inicio del arreglo
x.pop() Elimina el último elemento de un arreglo y lo devuelve
x.shift() Elimina el primer elemento de un arreglo y lo devuelve
x.slice(i,j) Devuelve un arreglo con los elementos extraídos entre las posiciones i y j. Si no se especifica el segundo parámetro se devuelve el resto del arreglo a partir de la posición inicial indicada.
x.includes(i) Verifica si un valor se encuentra en el arreglo

Las funciones Filter, Map, Reduce, Sort+

La programación funcional busca dar razón del cómo por medio de funciones. Para tratar arreglos Javascript incorpora otras funciones interesantes como son filter, map y reduce que pueden ilustrarse mejor con un ejemplo. Veamos…

const people = [{ "name": "John", "age": 25 },{ "name": "Ana", "age": 20 },{ "name": "Jane", "age": 30 }];

// filter by age > 20
const myfilter = people.filter( e => e.age > 20 );

// map printing the name
const mymap = people.map( e => { console.log(e.name); return e; } );

// reduce to acumulate ages
const myreduce = people.reduce( (sum, next) => sum + next.age, 0 );

// sort by difference between next elements
const mysort = people.sort( (x, y) => x.age - y.age );

// first element satisfies the criteria
const myfind = people.find(e => e.age >= 18)

// some element satisfies the criteria
const issome = people.some(e => e.age e.age > 25)

// every element satisfies the criteria
const isevery = people.every(e => e.age >= 18)

Peticiones Web con Javascript (fetch)

La sentencia fetch incluida en navegadores modernos (no aplica para Node.js) se usa para procesar peticiones web con Javascript, proporcionando una propuesta alterna, sencilla y mejorada respecto a lo que se lograba con el uso de XMLHttpRequest (implementado en librerías como JQuery). En tanto una petición Web devuelve una respuesta, ésta se puede gestionar en JSON (ejemplo: res.json()) o texto (ejemplo: res.text()) según se tenga conocimiento de la API concreta. Veamos el siguiente ejemplo:

    event.preventDefault();
    var form = document.forms[0];
    var data = {
        username: form['username'].value,
        password: form['password'].value,
    }

    fetch('/user/login', {
        method: 'POST',
        headers: { 'Content-type': 'application/json' },
        body: JSON.stringify(data)
    })
    .then(res => res.json())
    .then(data => {
        console.info(data);
    })
    .catch(err => {
        console.error(err.message);
    });

Si la API espera los datos de un formulario es probable que fetch requiera otro tipo de header, por ejemplo, de la siguiente manera:

    fetch('/user/login', {
        method: 'POST',
        headers: { 'Content-type': 'application/x-www-form-urlencoded; charset=UTF-8' },
        body: `username=${data.username}&password=${data.password}`
    })

Cuando se trata de solicitar datos directos, semejante a como se ingresa la dirección en un navegador (o para leer archivos de texto o JSON), aplica internamente el método GET y simplemente se usaría fetch con la dirección, así:

    fetch('/user/logged')

Del mismo modo, La sentencia continúa evaluando el resultado con .then(res => res.json()) (o .then(res => res.text()) si es texto) y otro .then para resolver la respuesta. Una mayor referencia puede ser consultada en el siguiente enlace: https://developer.mozilla.org/es/docs/Web/API/Fetch_API/Utilizando_Fetch

Ahora usemos fetch con await, colocándolo dentro de una función con async. Veamos el ejemplo:

async function loadData () {
    try {
        const api = await fetch('/user/logged');
        const data = await api.json();
        console.info(data);
    }
    catch (err) {
        console.error(err.message);
    }
}

Una alternativa popular a fetch es la librería axios, la cual reduce el paso de transformación de datos de respuesta (api.json()) y simplifica el modo de enviar los datos,. Por ejemplo, cuando se trata de un POST se puede enviar url, data, options (que puede incluir headers) o en un modo alternativo puedes enviar un objeto e indicas el método como un atributo (además de los otros parámetros mencionados)

El DOM & Javascript (sólo en el navegador)

El “Document Object Model” (DOM) es la estructura de elementos de HTML, se puede pensar como un directorio gerárquico dado el anidamiento de los elementos o etiquetas (tales como: <body>, <div>, <p>, <span>, <h1>, <table>, <form>, <input>, etc.). Con Javascript del lado del navegador es posible acceder al DOM y manipularlo, es decir, cambiar el HTML en tiempo de ejecución. Precisamente, al ser una labor algo avanzada o que demanda funciones para obtener una mayor dinámica e impacto visual, han surgido librerías como JQuery hace más de una década atrás, pero con la modernización de éste lenguaje de “script” existe actualmente una nueva generación de librerías y marcos de trabajo que facilitan esta labor, tales como: React, Angular, Vue y Riot (ésta última, de gran acogida en OnMind entre otras cosas por tratarse de una implementación ligera siendo cercana al HTML). He aquí una referencia de 10 funciones frecuentes para manipular el DOM directamente.

Función Descripción
document.getElementById Obtiene el elemento dado un identificador
document.getElementsByName Obtiene un arreglo de elementos dado un nombre. Por ejemplo cuando se usa input o form con el atributo name se puede usar este método.
document.querySelectorAll Devuelve una selección de elementos (arreglo) basándose en una expresión que puede corresponder a una clase, etiqueta o identificador (simplificando el uso de document.getElementsByClassName, document.getElementsByTagName y document.getElementById respectivamente). Puede usarse document.querySelector para retornar el primer elemento encontrado.
document.forms Obtiene un arreglo de los formularios del documento
document.body.style En realidad se puede acceder a document.body en general, pero con style es posible establecer atributos del cuerpo del documento
document.createElement Crea en memoria un elemento HTML para disponer de éste, asignándose a una variable, por ejemplo, x.
x.setAttribute Establece atributos de un elemento dado, dónde x es la variable que da referencia de un elemento HTML.
x.getAttribute Obtiene un atributo de un elemento dado, dónde x es la variable que da referencia de un elemento HTML.
x.appendChild Agrega al DOM un elemento creado previamente con createElement, dónde x es la variable que da referencia de un elemento HTML.
x.innerHTML Obtiene el contenido interno, y de tratarse de una asignación lo reemplazaría por un nuevo contenido, dónde x es la variable que da referencia de un elemento HTML.

Ejemplo de Expresiones Regulares (RegExp)

Las expresiones regulares puede ser usadas para determinar patrones en una cadena de caracteres. Para esto se inicializa una variable con RegExp(). Por ejemplo:

    var str = 'without inner spaces'
    var re = new RegExp(' ','g')
    console.log(str.replace(re,''))  // withoutinnerspaces

El código de arriba reemplaza todos los espacios por una cadena vacía, de modo que queda sin espacios. Debe recordarse que la función replace no reemplaza todas las ocurrencias y con esto se resuelve ese caso.

Proyecto de Inicio Rápido

Pensando en usar Javascript en el navegador, se requiere abrir un archivo HTML e incorporar un contenido inicial, por ejemplo, index.html.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Template with Bootstrap styles</title>

    <!-- Bootstrap Styles -->
    <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet" media="screen">
  </head>
  <body>
    <div class="jumbotron">
        <h1>Hi there!</h1>
        <button type="button" class="btn btn-outline-primary" onclick="alert('Dont use the alert anymore because its old practice!');">
            Click me
        </button>
    </div>

    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>
  </body>
</html>

NOTA. Si bien es un ejemplo sencillo que se puede abrir directamente con un navegador (el cual usaría el protocolo file://), lo mejor es contar con un entorno semejante a un sitio Web (protocolo http://). Por ejemplo, estando dentro de la carpeta ejecutas desde una consola el comando python -m SimpleHTTPServer 8000 y luego en tu navegador indicas como dirección localhost:8000. Esto funciona si tu sistema tiene instalado python, una manera de saberlo es intentarlo, sino descargalo. Cuando se tiene otra página o se da un nombre de archivo distinto a index.html, la dirección sería: http://localhost:8000/page.html.

El archivo creado simplemente proporciona una plantilla preestablecida usando una librería de estilos denominada Bootstrap para mejorar la presentación de un modo ágil (sin necesidad de saber sobre CSS3). Para entrar en materia en nuestro asunto, usaremos una etiqueta <script> nueva dónde se incorpora nuestro código Javascript, insertando antes de cerrar el body, lo siguiente:

    <script>
        let n = 0;

        function buttonHandle(e) {
            let x = e.target;  // here 'target' contains the element attributes
            n++;
            x.innerHTML = '<strong style="color: green;">&check;</strong>' + n;
        }
    </script>

Para usar nuestra función buttonHandle, se deberá modificar el evento onclick del elemento button quedando éste de la siguiente manera:

        <button type="button" class="btn btn-outline-primary" onclick="buttonHandle(event)">
            Click me
        </button>

En el evento onclick se envía el escenario que tiene lugar con la variable event, de modo que nuestra función lo recibe y modifica el contenido del botón con el número acumulado de veces que se ha dado clic sobre éste. Ten presente estar usando un navegador moderno.

Guarda y actualiza la página (F5) para que observes el comportamiento.

Como segundo ejemplo, usaremos otra plantilla y haremos otro cambio en nuestro script, de modo que reemplazamos todo el contenido de nuestro archivo por el siguiente:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Template with Semantic-UI styles</title>

    <!-- Semantic-UI Styles -->
    <link href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.5.0/semantic.min.css" rel="stylesheet" media="screen">
  </head>
  <body>
    <div class="ui placeholder segment">
        <div class="ui icon header">
            Hi there!
            <i class="smile yellow icon" id="animate"></i>
        </div>
        <button class="ui primary button" onclick="buttonHandle(event)">Click me</button>
    </div>

    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.js"></script>
    <script>
        let n = 0;

        buttonHandle = (e) => {
            e.preventDefault();  // prevents triggering without a click
            let x = e.target;
            n++;
            x.innerHTML = '<i class="thumbs up icon"></i>' + n;
            $('#animate').transition('bounce');  // $('#animate') is like JQuery gets an element by id
        }
    </script>
  </body>
</html>

Observa el código e identifica las variaciones. En esta ocasión se ha usado Semantic-UI para aprovechar los estilos que proporciona, semejante a la anterior plantilla nos evita tener que escribir código CSS3. Puedes revisar su documentación que es un tema aparte. Combina además algo de JQuery, librería que se usaba frecuentemente para múltiples utilidades, aunque viene reduciéndose su dependencia en los últimos años siendo posible aplicar efectos visuales con CSS3.

Por otra parte, se han usando funciones tipo arrow, es decir, nuestra función se ha expresado de la siguiente manera buttonHandle = (e) => {}. Ten presente estar usando un navegador moderno.

Para finalizar y hacer algo con datos, carga y analiza el siguiente código:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Template with Semantic-UI styles</title>

    <!-- Semantic-UI Styles -->
    <link href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.5.0/semantic.min.css" rel="stylesheet" media="screen">
    <!-- HighlightsJS -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.15.9/styles/ocean.min.css">
  </head>
  <body>
    <div class="ui text container">
        <br/>
        <form onsubmit="onSend(event)">
            <div class="ui labeled action fluid input">
                <div class="ui label">https://swapi.co/api/</div>
                <input type="text" placeholder="people/1/">
                <button class="ui icon button">
                    <i class="search icon"></i>
                </button>
            </div>
        </form>
        <div class="ui placeholder segment" id="holder">
            <div class="ui icon header">
                <i class="search blue icon"></i>
                Try it!
            </div>
        </div>
        <pre>
            <code style="max-height: 300px; overflow-y: scroll;"></code>
        </pre>
    </div>

    <script src="https://code.jquery.com/jquery-3.6.1.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.5.0/semantic.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.15.9/highlight.min.js"></script>
    <script>
        onSend = (e) => {
            e.preventDefault();
            let input = document.querySelector('input');
            let uri = 'https://swapi.co/api/' + input.value;
            if (!input.value)
                uri = 'https://swapi.co/api/people/1/';

            fetch(uri)
            .then(res => res.json())
            .then(data => {
                let holder = document.getElementById('holder')
                if (holder) holder.remove();
                let code = document.querySelector('code');
                code.innerHTML = JSON.stringify(data, null, "  ");
                hljs.highlightBlock(code);
            })
            .catch(err => {
                document.querySelector('code').innerHTML = `${uri} => ${err.message}`;
            });
        }
    </script>
  </body>
</html>

Ejemplo de Lazy Loading

const targets = document.querySelectorAll('img');

const loading = target => {
    const io = new IntersectionObserver((items, observer) => {
        items.forEach(item => {
            if (item.isIntersecting) {
                const img = item.target;
                const src = img.getAttribute('data-src');
                img.setAttribute('src', src);
                img.classList.add('fade');
                observer.disconnect();
            }
        });
    });

    io.observe(target);
}

targets.forEach(loading);

Dejo como referencia un video introductorio que puede ser de interés.


© 2019 by César Arcila