duduromeroa.com

#Javascript, #DOMJs, #webinteractiva, #Interfaces, #duduromeroa, #Guayaquil

Javascript

Programación para web (1/8): 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.

George Póyla, matemático. Resolver problemas de diseño. www.duduromeroa.com

Por Eduardo J. Romero Andrade
| Guayaquil



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, como calcular y mostrar la interacción y las animaciones durante un videojuego.

Programar significa crear procesos e instrucciones que sean reconocibles por sistemas. Programar también refiere a insertar, leer, transformar y mostrar datos hacia un sistema.

A diferencia del lenguaje humano –variado, subjetivo– un lenguaje de programación debe ser lógico y preciso.

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

El anterior requerimiento podría expresarse, con algunas limitaciones, de la siguiente forma mediante programación falsa, que es una forma de plantear un problema lógico de programación en forma de afirmaciones breves, alejadas de la sintaxis de un verdadero lenguaje de programación:

Código falso

INICIO
Objeto : manzana;
método : pedido(objeto);

para cada inicio de método : alertar y confirmar;

si método existe{
      activar método;
      confirmar } 
      si no existe {
      alertar
      }

      pedido(objeto){
      entender el pedido,
      buscar manzana,

            si manzana existe{
            seguir
            } si no existe {
            alertar
            }
      recoger manzana,
      entregar manzana
      }

alerta : metodo hallado;
alerta : metodo activado
alerta : metodo finalizado
alerta : manzana entregada

FIN

En otras palabras, un ser humano podría comprender una orden, interpretarla, pedir aclaraciones y confirmaciones antes y después de realizarla. Un sistema, en cambio, requiere recibir las órdenes en un lenguaje previamente interpretado, actualizado, dando precisiones de datos y estableciendo acciones de confirmación, todo eso sin ambigüedades y con una economía de palabras muy limitada. El lenguaje que la web interpreta para recibir datos, órdenes y acciones para alterar esos datos es Javascript.

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

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.





EXPRESIÓN, INICIALIZACIÓN, DECLARACIÓN

Expresar o sentenciar es usar una unidad de código para crear valores. Las expresiones se separan con punto y coma. Por ejemplo, en matemáticas la expresión '10 + 100;' crea el valor 110.

Las declaraciones vinculan un nombre identificador a un valor de texto o numérico. Declarar también refiere a crear o identificar una función. Una declaración SIN un valor inicial es leída por el sistema como undefined.

Inicializar es dar un valor –numérico o alfanumérico– a una expresión.

Entonces, expresar es usar el código para crear datos; declarar es identificar un valor para luego referenciarlo; e inicializar es dar un valor a un identificador.

/* Declarador de variable: expresión mínima que 
por sí sola no crea valores. */
var;

// Declaración mínima
var num;

// Inicialización: dar un valor
var num = 20;

// Expresión y declaración: crea un resultado
var num = 20+20;
// -> 40

/* Expresiones que declaran identificadores 
y crean inicialización con valores */
var nomb = "Edu", 
var apell = "Romero"
var edad = 45;

/* Expresión condicional formado por 
otra expresión con dato declarado */
if (nombre == "dudu") { alert("Hola Dudú"); }
// Muestra 'Hola Dudú'

Para w3schools cada sentencia o declaración puede contener:

  • Valores
  • Operadores
  • Expresiones
  • Palabras clave
  • Comentarios



INVOCAR VALORES

En JS un valor debe ser declarado –darle un identificador o nombre–; para luego ser inicializado –darle un valor inicial– y para finalmente ser referenciado (recuperar ese valor en memoria para luego operar).

CUIDADO: Cada declaración debe tener un nombre identificador diferente. Mayúsculas y minúsculas se leen diferente.
// Identificador con mayúsculas
var NOMBREPERSONA = 10;

// Identificador con inicia con guión
var _nombrePersona = 20

// Identificador con variantes
var nombrEPersonA = 10;

// Invocamos valores
console.log(NOMBREPERSONA);
// 10

console.log(_nombrePersona);
// 20

console.log(NOMBREPERSONA == nombrEPersonA);
/* true. Ambos identificadores 
tienen el mismo valor */

Abajo, en las dos primeras líneas los valores se guardan en memoria pero aún no son accedidos. Luego se invoca solo el nombre identificador correcto de cada valor.

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

return permite que una expresión retorne un valor especificado dentro de una función.

  • No es necesario usar la función console.log() antes del return. Recordar que console.log() devuelve un dato para evaluación solamente.
  • return no siempre necesitará de una expresión para devolver un valor válido. Ya que también puede devolver un valor undefined aunque no se devuelva ningún otro valor.

En Javascript

/* Esta función retorna undefined 
porque a 'return' no le sigue una expresión */
function num(x) {
 x * x;  
return;
}
console.log(num(5));
//-> undefined

// ****** Esta expresión sí retorna un valor *****
function num(x) {
return x * x;  
}

let dato = num(5);
console.log(dato);
//-> 25

Por otro lado, la diferencia entre usar console.log y la sentencia return en la siguiente: la función console.log() si bien muestra un valor en consola, no devuelve un valor a un invocador o llamado de referencia ni tampoco finaliza la ejecución de una función. En cambio, la sentencia return sí prepara un valor para ser invocado desde fuera de la función y sí detiene la ejecución de la función luego de ser invocada.

  • console.log() no devuelve un valor a un invocador externo ni detiene la ejecución de una función; pero return sí lo hace.
/* La siguiente función usa console.log() 
para mostrar un valor. Ejecutará cada invocación.*/
function a(b){
let x = b + b;
console.log(x);
console.log(x+x);
console.log(x*x);
console.log(x/x);
// return x;
}
// Invocación
a(10);
//-> 20
//-> 40
//-> 400
//-> 1

/* ERROR: No obtiene el valor de 'x' */
// console.log(x);
// ReferenceError

/* usando return;  */
function a(b){
let x = b + b;
return x;

// Esto ya no se ejecuta
console.log(x);
console.log(x+x);
console.log(x*x);
console.log(x/x);
}

console.log(a(10));
// 20

Si return no halla un valor retornará la propiedad undefined. Eso indica que un valor no ha sido asignado a una variable o no ha sido identificado con un nombre, es decir; no está declarado.

Para Svekis y Putten[5], gracias a return todo valor creado dentro de una función –es decir, un valor local– es accedido desde cualquier sitio del código. Sin return, se ejecutará la función pero existirá siempre un valor sin definir.

/* Función con parámetro para 
insertar un valor */
function numero(a){

/* Altera valor insertado al 
ingresar valor de argumento */
let operado = a*a;

// Se pide mostrar argumento
console.log(operado);
}

// Invocar función y dar argumento
console.log(numero(3));
// -> Muestra valor 9
// -> Pero tamb muestra undefined

Con return al final de la función nada queda pendiente, pero solo se podrá ejecutar un envío y una sola vez.

Usando una función con return para mostrar un valor en la pantalla en vez de la consola:

En HTML

<!-- Se usa un elemento HTML para usarlo como nodo -->
 <p id="mostrar"></p>

En Javascript

// Definir función, argumentos y operación
function operar(a, b) {
  return a * b;   
}  

/* Crear variable y guardar función con argumentos */
/* Ojo aquí ya estamos invocando */
let x = operar(4, 3); 

/* (ojo igualdad): Al apuntar a un elemento id 'mostrar', 
insertar el resultado de la función 'operar' */
document.getElementById("mostrar").innerHTML = x;

// -> Se muestra en pantalla el valor 12

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; };
// Función con parámetro
function nombre(a){
// Solo devuelve booleano true
return true;
}
// Invoca función con un valor cualquiera
console.log(nombre(11));
// -> true

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 enviará el paquete solo cuando él este presente y cuando la orden –la invocación– este activa.

// 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

En otro ejemplo: accedemos primero al valor del argumento y luego, al valor del mismo argumento desde return, pero operado. La función se cierra luego de eso:

// Función con parámetro
function nombre(a){
// Accedemos al valor de 'a'
console.log(a);
// Devolvemos el valor de 'a' operado
return a*a;
}
// Invoca función con un valor cualquiera
console.log(nombre(5));
// -> 5     --> El valor de 'a'
// -> 25    --> El valor de 'a' multiplicado

En cambio, acceder al valor del argumento desde fuera de la función y sin return dará error.

function nombre(a){
// Devolvemos el valor de 'a' operado
return a*a;
}
// Invoca función con un valor cualquiera
console.log(nombre(5));

// DA ERROR: 'a' no está definido
console.log(a);
/* Es imposible llamar al valor de 'a'
desde fuera de la función 'nombre*/

HOISTING: ALZADO DE VALORES

El anglicismo 'hoisting' refiere a alzar, elevar o izar algo. He inventado un verbo en español para eso: 'primerizar'. En JS el hoisting refiere a cómo se referencian valores o funciones que aún no han sido declaradas.

En JS, hoisting o primerizar –según mi nuevo verbo– significa poner en primera línea aquella expresión declarada que debería ser leída primero, pero que se ha declarado mucho después de su invocación:

En Javascript

// Expresión 'A' invocada aquí

// Código.....
// Código.....
// Código.....

/* Expresión 'A' declarada aquí: JS la manda 
a la primera línea para ser leída y operada.*/ 

/* Invocamos valor de 'x'. Este valor aún no existe, 
por lo que dará un mensaje de 'undefined' */
console.log(x); 

// -- Solo lo declarado se primeriza --
/* Aquí declaramos a 'x' como una variable; pero aún 
no la hemos inicializado con un valor. Sin embargo, 
'x' ya está con hoisting, es decir, está lista 
para ser leída en caso de ser alimentada con un valor */
var x;

/* Aquí 'x' ya está inicializado.  
Pero ese valor no esta primerizado, 
solo su declaración. 
Por lo tanto, 'x' solo le toca leer el valor de 10 */
x = 10;

/* Por lo que al invocar 'x', ahora sí, 
podemos obtener el valor y mostrarlo */
console.log(x); 
//-> 10

El hoisting solo eleva las declaraciones; no eleva inicializaciones:

  • var x -> valor solo declarado. Puede ser elevado
  • x = 5 -> valor solo inicializado. No puede ser elevado
  • var x = 5 -> valor declarado e inicializado. No puede ser elevado

¿Por qué solo se elevan las declaraciones y no las inicializaciones?:

  • Para permitir que solamente las declaraciones sean invocadas desde cualquier ubicación en el código, aún cuando no hayan sido declaradas todavía.
  • Elevar una expresión inicializada obligaría a evaluarla primero sin saber, por ejemplo, qué tipo de variable o función son o si tienen un valor sin errores de sintaxis o de dato.

En Javascript

// PRIMERIZADO CON VALOR DECLARADO
// Se invoca algo que aún no se declara
console.log(x); 
/* Se declara valor: pero solo se sube el 
identificador 'x', no su valor inicializado */
var x = 10;
// ReferenceError: x is not defined

/* Para solucionar eso, primero se debe 
declarar e inicializar; luego, invocar*/
var x = 10;
console.log(x); 
//-> 10

/* Aquí no es necesario el hoisting porque 
la declaración, la inicialización y la invocación 
es lógica y ordenada */

// Declarar
var numero;

// Inicializar
numero = 100;

// Invocar
console.log(numero);

// Otro ejemplo
// Se declara función
function saludo(a){
// Retorna valor
return a;
}

// Se invoca función
console.log(saludo(3));
//-> 3

Luego, probar dónde JS permite el alzado de la expresión desde una función, una variable inicializada y una variable declarada.

// *** HOISTING CON FUNCIONES ***
// SÍ podemos primero invocar para luego inicializar
saludar("holi");

/* Gracias al hoisting esta función sube
para ser leída primero */
function saludar(a){
console.log(a);
return;
}
//-> holi

//---- ---- ----

// *** HOISTING CON VARIABLES INICIALIZADAS ***
/* NO es posible invocar primero e inicializar después */
console.log(b);
/* ES decir, JS NO eleva lo inicializado */
var b = 100;
// -> undefined. Primero inicializar para luego invocar

//---- ---- ----

// *** HOISTING CON VARIABLES DECLARADAS ***
/* SÍ podemos declarar para luego inicializar */
c = 10;
/*  JS SÍ eleva lo declarado */
var c;
// Para luego invocar
console.log(c);
// -> 10

El hoisting no funciona con variables inicializadas –es decir, con variables a las que YA se les asignó valor:

/* INCORRECTO */
/* Invocar primero...*/
console.log(numero);
/* inicializar después. */
numero = 100;
// ERROR DE REFERENCIA

// _ _ _ _ 

/* CORRECTO */
/* Inicializar...*/
numero = 100;
/* para luego invocar...*/
console.log(numero);
// -> hola

Según mdn web docs, todo lo declarado es alzado; es decir, llevado a la memoria del sistema para ejecutarlo en orden de declaración.

En otro ejemplo: gracias al hoisting la función 'saludo(a)' es leída como si hubiera estado declarada desde el inicio del código.

// Invoco algo que no está declarado
saludo("Hooli");
//-> ERROR: No definido

// Pero finalmente declaro la función...
function saludo(a){
return console.log(a);
}
// --> Hooli
  • Declarar es dar un identificador a un tipo de dato.
  • Inicializar o asignar es dar un valor a esa declaración.
 // Se declara una variable pero NO se la inicializa
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;

/* Y aquí se la REinicializó con otro valor */
numero = 100/2;
}

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

En el ejemplo de abajo, si bien la función 'nombre("Mau")' 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'

Todo lo anterior nos lleva a recordar que: una variable dentro de una función –es decir, con un contexto local– será leída PRIMERO por JS antes que otra variable ubicada fuera de esa función –es decir, con un contexto global–.

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

En otro ejemplo JS leerá como indefinido un valor que haya sido llamado pero sin haber sido inicializado antes. Similar a decir 'tengo 4 manzanas en la cesta' sin tener la cesta ni las manzanas.

// 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, el hoisting en JS eleva o primeriza toda declaración –excepto las inicializaciones– de variable o funciones. Sin embargo, siempre será mejor práctica usar variables o funciones cuando sabemos que ya han sido declaradas.




TIPOS DE VALORES: NÚMEROS

Un tipo es un valor operable para crear otro nuevo valor. Para Antani y Stefanov[4] "todo lo que no sea tipo de dato es un objeto" en JS. Los tipos son:

  • 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 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).

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 aritmética de ejecución de operaciones. Según ese orden, la multiplicación y la división se ejecutarán antes que las sumas y restas. 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 (%) solo devuelve el valor residuo de una división.

// Ejemplo con residuo
var num = 2;

// num dividido para 2
console.log(num % 2);
/* Operador % SOLO arroja el residuo de la división */
// -> 0

/* Si num / 2 da como residuo el cero */
if (num % 2 == 0){
// Si es true
console.log(num + " es par")
// Si es false
} else {console.log(num + "Es impar")
};
// Es par

Subir al inicio Avanzar



BIBLIOGRAFÍA:
    3ra edición. 2018. Marijn Haverbeke. No starch press.
    Mozilla Foundation.
    David Flanagan (2020). Editorial O'Reilly.
    Ved Antani, Stoyan Stefanov (2017). Packt Publishing.