duduromeroa.com

Javascript

Programación para web (parte 1): Javascript, expresiones, declaraciones, tipos de valores


Esta sección aportará con estudio y análisis de la sintaxis del lenguaje de programación Javascript, creado en sus fases iniciales por el estadounidense Brendan Eich en 1995.

Estudio del lenguaje de programación javascript. www.duduromeroa.com, Guayaquil - Ecuador
George Póyla, matemático. Resolver problemas de diseño. www.duduromeroa.com

Por Eduardo J. Romero Andrade
| Guayaquil

Twitter   JS para Guayacxs   duduromeroa@gmail.com




Breve introducción: código y sintaxis en programación

Los lenguajes de programación son el idioma que las computadores interpretan para ejecutar acciones entre máquina y usuario. Para un computador, una acción podría ser el guardar datos, como dos números; o activar un proceso, como sumar 10 + 3; o ejecutar un resultado, como mostrar el número 13 en pantalla. Así mismo, para un computador una acción puede ser una suma de eventos, procesos y resultados, como calcular y mostrar la interacción y las animaciones durante un videojuego.

Programar significa crear procesos e instrucciones que sean reconocibles por sistemas mecánicos-electrónicos y reutilizables por seres humanos. Programar también refiere a insertar, leer, transformar y mostrar datos hacia un sistema.

A diferencia del lenguaje humano –variado, subjetivo y de múltiple interpretación–, un lenguaje de programación debe ser lógico y preciso. Y debe ser ejectuado desde respuestas binarias: cero y/o uno; sí, no; cierto y/o falso.

Por ejemplo, en lenguaje humano, podemos pedirle a otra persona lo siguiente:

  • "Por favor, pásame la manzana que está encima de la mesa".

La respuesta a ese pedido dependerá de factores como el idioma –la otra persona debe entenderlo–, el contexto –si la manzana es real, imaginaria o una representación, como un dibujo–, y la aceptación de ese pedido –es decir, que otra persona esté presente y acepte ejecutar la acción–.

Para una máquina, el mismo pedido debería ser claro, preciso y con opciones coherentes posibles. Nada al azar. Sin embargo, esos requerimientos de precisión y lógica transforman el pedido a un conjunto de frases mucho más extensa:

  • 1. "¿Alguien necesita una manzana?"
  • 2. "¿Existe una manzana?"
  • 3. "Si la respuesta es sí, proceda. Si la respuesta es no, cancele."
  • 4. "¿Dónde está la manzana?. ¿Es accesible?. Si la respuesta es sí, proceda. Si la respuesta es no, cancele el pedido."
  • 5. "Ubica la manzana"
  • 6. "¿Existe un método para obtener la manzana? Si la respuesta es sí, proceda. Si la respuesta es no, cancele y alerte de esa ausencia de método (...) "
    • Etc...

Para un sistema lógico y no consciente de su entorno es difícil interpretar un pedido. Es decir, un sistema no vivo carece de la experiencia y de los sentidos sensibles para determinar qué hacer, cuándo, cómo y a quién. Es por eso que los lenguajes de programación le otorgan a las máquinas el contexto y las instruccioes necesarias para ejecutar y responder.

Finalmente, para que un sistema interprete un lenguaje de programación específico, el anterior requerimiento podría expresarse, con algunas limitaciones, de la siguiente forma:

duduromeroa-portada-tema

El formato de signos como los de igualdad, punto y coma, las palabras y la estructura se conoce como sintaxis del lenguaje. Cada sintaxis de cada lenguaje tiene una función determinada. Comprenderla ayudará a entender todo el lenguaje y su alcance. Javascript tiene una sintaxis específica, pero similar a otros lenguajes de programación creados décadas atrás.

¿Qué es Javascript?

Javascript –Java, por la referencia a otro lenguaje de programación; y script, anglicismo usado para definir que algo es redactado según ciertas normas– es un lenguaje de programación que se interpreta y se ejecuta desde la internet y desde toda página web alojada en ella. Su evolución también permite usar Javascript para crear aplicaciones instalables en dispositivos móviles, crear interactividad compleja como en videojuegos, y vincularse con lenguajes web de marcado como HTML; y de diseño y estilo, como CSS, entre muchas otras funciones.

De aquí en adelante, explicaré progresivamente y desde un nivel intermedio-avanzado muchas de las características de JS.

Si el lector necesita referencias iniciales de HTML y JS, recomiendo revisar este vínculo o cualquier otro recurso que explique los fundamentos más básicos de esas tecnologías.

Finalmente, en la bibliografía –al final– adjunto los libros y sitios web consultados.





EXPRESIONES Y DECLARACIONES -O SENTENCIAS EN JS

Un ladrillo por sí mismo no es funcional. Pero un conjunto de ellos puede contruir hasta una catedral. En JS, una expresión por sí sola no crea nada. Pero una combinación de ellas sí.

Por lo tanto, en JS las expresiones son palabras y valores aislados que, juntos, construyen nuevos valores. Si varios ladrillos levantan una pared, y muchas paredes levantan pisos; así mismo muchas expresiones pueden crear oraciones que generen instrucciones para un sistema electrónico.

Para w3schools las sentencias o declaraciones en JS son órdenes expresadas en sintaxis especial que el navegador interpreta y ejecuta. Se separan con punto y coma para diferenciarse de otras. Así mismo, cada sentencia o declaración puede contener:

  • Valores
  • Operadores
  • Expresiones
  • Palabras clave
  • Comentarios
/* Ejemplo de expresión: Palabra clave usada 
para guardar un dato en memoria. 
Por sí sola, no genera nada */
var

/* Ejemplo de declaración: conjunto de 
expresiones que juntas, guardan un valor 
en memoria */
var nombre = "dudú";

/* Otras declaraciones */
var nombre = "dudu";
if (nombre == "dudu") { alert("Hola Dudú"); }
// Muestra una alerta con las palabras Hola Dudú



INVOCAR VALORES

Gracias a la memoria humana escuchamos una canción que nos gusta, averiguamos su nombre e intentamos memorizarla para luego, recordarla. Así mismo, en JS cada trozo de valor es declarado –darle un identificador o nombre–; luego es inicializado –guardar ese valor en el identificador– y finalmente podrá ser invocado (recuperado desde memoria para aprovechar ese dato).

CUIDADO CON LAS ALTAS Y BAJAS EN LOS NOMBRES IDENTIFICADORES DE LAS VARIABLES
/* Ninguno de estos nombres 
serán iguales entre sí */

// Bajas
var nombrepersona = "a";

// Altas
var NOMBREPERSONA = "b";

// Inicia con guión. 
var _nombrePersona = "c";

// Algo raro
var nombrEPersona = "d";

/* Los valores se guardan 
en memoria, pero aún no 
son accedidos */
var cancionQueMegusta = "Woodstock"
var autor = "CSN";

/* Valores invocados */
/* Los valores se invocan desde el 
identificador o nombre de la variable. 
NO desde el valor */
alert("La canción que me gusta es " + 
cancionQueMegusta + 
"y la toca " + 
autor );

/* Mensaje: La canción que
me gusta es Woodstocky la toca CSN */ 

SENTENCIA RETURN

Antes de revisar otros conceptos como las funciones, los arreglos y los objetos, revisaremos a fondo la sentencia return.

return permite que un valor especificado dentro de una función sea 'olfateada' desde fuera de esa función al invocarla. Además, return finaliza la ejecución de una función apenas ser invocada. Ya que cada función solo puede retornar un valor a la vez, return envía a la invocación ese valor y luego de eso, cualquier otra ejecución es detenida. Es decir, una vez activado return nada más ocurrirá en la función –hasta nueva ejecución–.

Para Svekis y Putten[5], gracias a return todo valor local de una función es accedida desde fuera –lo que normalmente es imposible de hacer–. En otras palabras, return desbloquea esa prohibición de acceso a valores de una función.

return 'entrega' un valor determinado desde la expresión; pero si ese valor no existe, retornara la expresión indefined

// Función 
function llamar(numero) {
  if (numero > 100) {
// Retorna valor    
return numero;
  }
// Retorna valor
  return "Es menor";
}

// Se envia argumento
/* Como return ya envió un valor, 
NADA MÁS será ejecutado */
console.log(llamar(1));
// --> Es menor
 
// Llamado de función:
// Se envia NUEVO argumento
/* Como return ya envió un valor, 
NADA MÁS será ejecutado */
console.log(llamar(101));
// --> 101

Otras formas en que return 'entrega y detiene ejecución' son las siguientes:

  • return;
  • return true;
  • return false;
  • return valor;
  • return valorA * valorB / valorC;
  • return function calc(x) { return x * 42; };

Es necesario entender porqué el retorno de un valor no es igual a mostrar un valor. La sentencia return prepara un valor que será alcanzable al invocar una función. Ese es un trabajo interno e invisible para nosotros. El valor retornado no será visible a menos que una función sea invocada.

En el ejemplo inferior, el único trabajo de la función 'ejecuta()' y su parámetro 'a' es la de multiplicar un valor por mil. Una vez retornado -de forma invisible para nosotros- ese valor a la invocación 'console.log(ejecuta(10));' solo la función 'console.log()' mostrará ese valor. El resto del contenido de la función no será ejecutado.

Finalmente, return es igual a un despachador de correo que, una vez empaquetado un envío, solo lo enviará cuando lo pidan -es decir, cuando la función sea invocada–. Sin invocación, el dato, el argumento o el resultado de una operación no será accedido, aunque sí estará listo para serlo.

// CON RETURN
function ejecuta(a){
/* Una vez devuelta esta operación, 
nada más será ejecutado */
return a * 1000;

// Lo siguiente ya no será ejecutado
return a * 2000;
console.log("Holaaaa!");
var numerote = 2000;
return console.log(numerote);
}

// Invoco e inserto argumento
console.log(ejecuta(10));
// --> 10000

// SIN RETURN
function ejecuta(a){
/*  Sin return, esta operación 
no se envia ni se muestra */
a * 1000;

/* Todo esto Sí */
console.log(a);
console.log("hola");
console.log("Holiiii");
console.log("Hellooouuu");
}

console.log(ejecuta(10));
// --> 10 (el argumento)
// --> hola
// --> Holiiii
// --> Hellooouuu
/* --> undefined
El resultado muestra 'undefined' porque 
esa función no está devolviendo un 
valor mediante ningún return. */ 

HOISTING: ALZADO DE VALORES

Explicaré esta importante característica de las variables en JS.

El anglicismo 'hoisting' refiere a alzar o izar algo. Y de forma general refiere a cómo se crean valores –desde variables y constantes– y cómo se ejecutan desde funciones en JS.

Recordemos también que JS interpreta o lee el código desde arriba hacia abajo y de izquierda a derecha. Es decir, ejecuta lo primero que 'lee', en ese orden. Además, debemos identificar lo declarado de lo inicializado o asignado:

 // Se declara una variable
var numero;

// Se asigna o inicializa con un valor a la variable
numero = 100;

Según mdn web docs, declarar el identificador de un valor o de una función –es decir, darles un nombre y un valor– significa llevarlos a la memoria del sistema antes de ejecutar cualquier otro código posterior. Para JS, guardar un dato en memoria significa alzarlo. Ese alzamiento es similar a llevar ese dato al inicio de todo.

 // Se declara una variable
let numero;

// Viene un bloque de código cualquiera
{ 
datoCualquiera: 1;
datoCualquiera: 2;
datoCualquiera: 3;
datoCualquiera: 4;
datoCualquiera: 5;
datoCualquiera: 6;

/* Recién aquí se ha 
inicializado la variable */
numero = 100;
}

// Aquí se ha invocado la variable
console.log(numero);
// -> 100
/* JS 'lee' el contenido de esa variable 
como si esta ya hubiera estado 
inicializada desde el inicio. */

En el ejemplo de abajo, si bien la función 'nombre' está colocada después de su invocación, JS toma esa función y la 'eleva' como si estuviera escrita al inicio del código.

// El valor recién está declarado e invocado...
// Pero la función 'nombre' aún no está creada
nombre("Mau");
// Mau

// La función recién es creada aquí
function nombre(ingresarNombre) {
console.log(ingresarNombre);
}

// Este código muestra 'Mau'

Debemos recordar que una variable dentro de un contexto -como una función- será leída por JS antes que otra variable fuera de ese contexto; por ejemplo, si esa variable estuviera fuera de una función.

En el ejemplo de abajo, el contenido de la variable 'musico' dentro de la función es mostrado primero antes que el contenido de la otra variable, con el mismo nombre y afuera de la función. Esto es porque JS da prioridad a la variable del primer ámbito. En este ejemplo, el primer ámbito es la función 'mostrar'.

// Identificador en ámbito global
var musico = "Julio Jaramillo";

function mostrar(m){
// Identificador en ámbito local: dentro de función
var musico = "Mireya Cesa";
return musico;
}

/* Llamamos hacia dentro de función */
console.log(mostrar("Crucks n Karnak")); 
// --> Mireya Cesa

Para JS, lo declarado es más importante que lo inicializado. JS no permite invocar el contenido de una variable si se la declara primero y se la invoca mucho después. Esto es porque el hoisting no aplica para inicializaciones. Toda variable que contenga un valor declarado tendrá prioridad por sobre una variable apenas inicializada. En otras palabras, primero declaramos y luego, invocamos.

// Invoco un valor que no ha sido creado aún
console.log(a); 
// Aunque lo haya creado luego, tampoco es obtenido
var a = 2;
/* Este código resulta en undefined. 
Es decir, se desconoce el contenido 
del identificador que está guardando el valor. */

En resumen, mediante el hoisting, JS eleva o primeriza toda declaración de variable o una función desde cualquier ubicación dentro del código. Y aún así, esas declaraciones podrán ser accedidos e invocados desde cualquier punto. Sin embargo, es mejor práctica invocar una variable cuando sabemos ya ha sido declarada.




TIPOS DE VALORES: NÚMEROS

Un tipo es un valor cuyo contenido es almacenable y operable para crear nuevo valor. En JS y en muchos otros lenguajes existen los siguientes tipos de valores, llamados primitivos por ser los más básicos para crear otros recursos más complejos:

  • Números, literales numéricos, enteros, racionales, decimales, negativos
  • Algunos números fraccionarios grandes –como Pi– deben ser tomados como aproximaciones y no como números precisos.[1]
  • Para Antani y Stefanov[4] "todo lo que no sea tipo de dato es un objeto" en JS.

Un tipo es un valor no alterable una vez definido. Para Haverbeke, si tenemos un dato alfanumérico "El texto dentro de él no puede ser cambiado [desde dentro o fuera del código]. Si tienes valor de nombre "gato", no es posible que otro código cambie un carácter en tu string para que deletree "rato". Lo que sí puede hacerse es usar ese valor para luego mostrarlo alterado, pero JS no permite alterar el valor fuente inicial. Esto es importante puesto que otros recursos de JS como los arreglos y los objetos sí pueden ser alterados una vez definidos.

Así mismo, para Haverbeke "Los valores de tipo string, número y Booleano no son objetos". Es decir, no guardan propiedades aunque sí contienen algunas que permiten modificarlos en su longitud o contenido (ver Objetos).

Todo valor numérico en un computador es posible gracias a la capacidad binaria de los transistores –micro electrónica que permite o impide el paso de electricidad de un punto a otro–. Un transistor equivale a un bit, con dos combinaciones posibles, pero con solo uno de esos valores disponibles para ser procesado:

    De dos valores, solo uno de ellos será tomado en cuenta:
  • Encendido o apagado
  • Abierto o cerrado
  • 1 ó 0 –uno o cero–

Ya que los números pueden ser infinitos, un computador necesita mayor capacidad de combinaciones. Todo lenguaje de programación moderno, como JS, permite hasta 18 mil trillones de combinaciones numéricas. Esto es, dos números con 18 ceros, la cantidad posible en sistemas con 64 bits de capacidad [1].

var numeroChiquito = 0.1;

// numeroGrandote = Nueve trillones
/* Separadores literales (guiones bajos 
entre cifras)*/
var numeroGrandote = 9000_000_000_000_000; 
var numeroTextual = "20";

/* Operaciones con valores
exclusivamente numéricos */
console.log(numeroChiquito + 1); //1.1

/* JS NO permite operar literales 
textuales –entre comillas–*/
console.log(numeroTextual + "1"); // 201
// Solo adjunta el número 1, NO lo suma.



PRECEDENCIA ARITMÉTICA (Operator precedence)

La precedencia es la jerarquía de ejecución de las operaciones aritméticas. Según ese orden, las operaciones de multiplicación y división se ejecutarán antes que las de suma y resta; y la suma "siempre se evaluará de izquierda a derecha."

En el ejemplo de abajo, dejemos que la precedencia aritmética pura haga lo suyo: primero multiplicar, luego sumar y restar.

 console.log(1 + 2 + 3 * 10 - 10);
// a) 3 * 10 = 30
// b) 30 + 2 + 1 = 33
// c) 33 - 10 = 23
// Resultado: 23

Otro ejemplos, abajo. Primero divide, luego suma y resta:

 console.log( 10 - 5 / 10 + 2);
// a) 5 / 10 = 0.5
// b) 0.5 - 10 = -9.5
// c) -9.5 + 2 = 11.5
// Resp: 11.5
// Primero se multiplica (4*11)
// Luego se suma (44+100)
alert( 100 + 4 * 11);
// R. 144
// Primero se divide
// Luego se suma y se resta
alert( 4 + 8 - 1 + 10 / 2);
// R. 16
// Para un mejor control, agrupar en paréntesis
alert( (4+8)-(1*10) / 2);
// R. 7

El residuo (%) -abajo- es esencial para determinar divisiones entre números pares o impares. El residuo es el valor sobrante de una división.

alert( 3 % 3);
/* R. 0 --> No sobra nada. Puesto que a cada 
unidad le corresponde exactamente otra.*/

alert( 5 % 3);
// R. 2 --> Sobra dos. 

Subir al inicio Seguir a la parte segunda



BIBLIOGRAFÍA:
    [1] Eloquent JavaScript 3ra edición. 2018. Marijn Haverbeke. No starch press.
    [2] MDN Web Docs, Javascript. Mozilla Foundation.
    [3] JavaScript: The Definitive Guide. David Flanagan (2020). Editorial O'Reilly.
    [4] Object-Oriented JavaScript Ved Antani, Stoyan Stefanov (2017). Packt Publishing.
    [5] Laurence Lars Svekis, Maaike van Putten. JavaScript from Beginner to Professional: Learn JavaScript quickly by building fun, interactive, and dynamic web apps, games, and pages (2021). Editorial Packt.