duduromeroa.com

Javascript

Programación para web: orientación a objetos, objetos, métodos, argumentos y parámetros


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

Twitter   JS para Guayacxs   duduromeroa@gmail.com



SIMULAR EL MUNDO CON OBJETOS, PROPIEDADES Y VALORES: EL MODELO DE LA PROGRAMACIÓN ORIENTADA A OBJETOS

En la vida real lo tangible y sus propiedades es algo que podemos llamar un objeto. Este tiene detalles en textura, peso, color y función que a su vez, pueden variar con respecto a otros objetos similares. Además, cada objeto podría ser alterado en sus propiedades por acción nuestra; por ejemplo, la forma de una manzana cambiará si la cortamos a la mitad. La propiedad forma se mantiene, pero su valor ha cambiado: de casi redondo a semi redondo. Veremos cómo el objeto en un contexto de programación va más allá de algo físico.

Aristóteles y la programación orientada a objetos

Un artículo de Adam Reed publicado en el 2003 explora las relaciones entre los conceptos de la programación orientada a objetos y la causalidad aristotélica; en la cual "las entidades –acciones o métodos– actúan y modelan el estado de una realidad –una clase–", y viceversa. El artículo está en inglés.



En lenguajes para cómputo, un objeto también tiene características alterables, intercambiables y ampliables; pero ese objeto será solo una simulación. Por ejemplo, un sistema artificial podría representar mediante el modelo orientado a objetos una manzana:

// Objeto manzana
var manzana = {
// Propiedades con sus valores
color: 'rojo', 
forma: "redonda", 
tipo: "fruta",
 
// Objeto anidado con propiedades y sus valores
origen: {
chilena: true, 
española: false, 
ecuatoriana: false
}};

// Invocando propiedades y valores
console.log("La manzana tiene color " + 
manzana.color);

Que un sistema simule a casi todos los elementos concretos o abstractos como agrupadores de propiedades y valores es el concepto básico para empezar a entender el modelo de programación orientada a objetos, conceptualizado a inicios de la década de 1960 por dos noruegos: un científico con experiencia en investigación nuclear llamado Kristen Nygaard; y por un experto programador de nombre Ole-Johan Dahl.

Según Antani y Stefanov[4], una analogía de lo literario con el modelo de programación orientada a objetos sería la siguiente:

  • Un sustantivo –como manzana– sería el objeto.
  • Los verbos –las acciones a ejecutar– serían los métodos.
  • Los adjetivos serían las propiedades y sus valores.
CLASES: INTRODUCCIÓN

Para Antani y Stefanov[4], en lenguajes de programación los "objetos similares se agrupan en clases", en la cual "una clase es una plantilla para creación de nuevos objetos" que permite instancia -o creación de un objeto–.

Cada objeto creado es una instancia –del neologismo instance–, cuyo sinónimo puede ser el de 'ejemplar' o 'especimen'. Una clase ahorra tiempo de creación porque permite instanciar muchos más objetos a partir de un conjunto de propiedades base, evitando el trabajo de duplicar propiedades en cada objeto por separado.

Sin embargo, Javascript no es un lenguaje con una sintaxis incorporada que cumpla a rajatabla el modelo de clases, como sí lo hacen otros lenguajes. ¿Por qué se dice que Javascript no lo hace?. Para Marin Benčević en "Why JavaScript is an OOP Language" una clase no contiene datos, puesto que es el objeto quién proporciona toda la información para las nuevas instancias. Benčević y Antani y Stefanov[4, p.16] dan más argumentos: en lenguajes orientados a objetos, "ellos [los objetos] son las estrellas del show, no las clases".

CONCEPTOS DE LA PROGRAMACIÓN ORIENTADA A OBJETOS

ENCAPSULACIÓN

Significa que un objeto guarda datos en forma de propiedades; y siempre se podrá realizar una acción –con los métodos– desde los datos.

Encapsular también refiere a proteger la forma en cómo los datos se operan dentro de un contexto, pero siempre habrá "formas de proteger los datos dentro de un objeto y lograr la privacidad"[4].

COMPOSICIÓN o 'Aggregation'

Significa que en lenguajes orientados a objetos es posible seccionar el código en parcelas más pequeñas, y así dar varias soluciones a problemas planteados uno cada vez.

HERENCIA

Para Antani y Stefanov[4] significa "reusar código existente" de la siguiente forma: cuando una clase madre contiene propiedades, todo lo creado a partir de allí copiará o heredará esas propiedades. Por ejemplo, en una clase de nombre persona cuyas propiedades sean 'edad' y 'sexo' copiará las mismas propiedades en cada nueva clase hijo nacido de la clase madre. En JS "los objetos se heredan entre objetos"[4], pero a su vez pueden actualizar los valores de propiedades según sean necesario.

Herencia > Clave extends

La clave extends señala que una clase deriva o es hija de una clase madre. En el ejemplo inferior, la clase 'Madre' tiene como hijo a la clase 'hijo'; y así mismo, 'hijo' nace de la clase 'Madre'.

Herencia > Clave super

La clave super llama al constructor desde la clase madre y dispone que propiedades y métodos se hereden de inmediato.

Finalmente, en el código de abajo se muestran tres acciones: primero, establecer la clase 'Madre' con dos propiedads iniciales –edad y sexo–. Segundo, establecer que 'hijo' deriva de la clase 'Madre', con una nueva propiedad 'colorCabello'. Y tercero, crear una variable que almacene cada nueva instancia con las propiedades heredadas y con nuevos valores.

 // Clase con propiedades asignadas
class Madre {
      constructor(edad, sexo){
      this.edad = edad;
      this.sexo = sexo;
      }
}
// Nace una clase hijo
class hijo extends Madre{
// Se heredan propiedades
constructor(edad, sexo, colorCabello)
      {
      super(edad, sexo);
// Se asigna propiedad
      this.colorCabello = colorCabello;
      }
}

/* Se crea variable que guarda 
nueva instancia que hereda propiedades. 
Pero aquí se agregan valores a esas propiedades */
let dudu = new hijo(35, "hombre", "negro");
console.table(dudu);
//-> Tiene 35 es hombre y cabello negro
/* 
┌──────────────┬──────────┐
│   (index)    │  Values  │
├──────────────┼──────────┤
│     edad     │    35    │
│     sexo     │ 'hombre' │
│ colorCabello │ 'negro'  │
└──────────────┴──────────┘
*/

/* Nueva variable que guarda otra instancia */
let adriana = new hijo(38, "mujer", "rojo");
console.table(adriana);
/* 
┌──────────────┬─────────┐
│   (index)    │ Values  │
├──────────────┼─────────┤
│     edad     │   38    │
│     sexo     │ 'mujer' │
│ colorCabello │ 'rojo'  │
└──────────────┴─────────┘
*/

POLIMORFISMO

Polimorfismo o 'muchas formas' se define como "llamar al mismo método o acción desde diferentes objetos"[4] en donde cada método tendrá un comportamiento diferente desde cada objeto.




OBJETOS (II)

Métodos del objeto Math

El objeto Math() contiene métodos internos que automatizan matemática compleja.

Math.pow()

Una de esos métodos es Math.pow(), cuyo primer argumento es un número –la base–; y el segundo argumento, las veces que se multiplica por sí mismo –el exponente–.

var a = Math.pow(2, 4);
console.log(a);
// --> 16

var b = Math.pow(2, 8/4);
console.log(b);
// --> 4

var c = Math.pow(2, -8);
console.log(c);
// --> 0.00390625

// Pide multiplicar el número 3 en 0.33 veces
console.log(Math.pow(3,1/3));
// R. 1.44224957...

Math.round()

Devuelve la conversión de un número decimal a un número entero, según lo siguientes criterios:

  • Si el decimal es mayor o igual a 0.5 --> la conversión resultará en el entero inmediato mayor: uno.
  • Si el decimal es menor a 0.5 --> la conversión será al entero inmediato menor: cero.
/* Decimal MENOR a 0.5...   */ 
console.log(Math.round(0.4));
// --> 0

console.log(Math.round(-0.2));
// --> -0

/* Decimal MAYOR o IGUAL a 0.5...   */ 
console.log(Math.round(0.6));
// --> 1

console.log(Math.round(3.1000));
// --> 3

console.log(Math.round(3.6000));
// --> 4

Operar desde una variable puede ser útil:

var calc = 55.332/11.4344;
console.log(calc);
console.log( Math.round(calc) );
// 4.8 –-decimal mayor a 0,5 --> redondea a 5

Math.ceil()

Devuelve la conversión decimal desde 0.1 a 0.9 hacia el entero inmediato más alto. A diferencia de Math.round(), que redondea hacia el entero más alto desde 0.5 a 0.9; Math.ceil() redondeará desde un decimal más bajo (0.1).

// Decimal
var num = 0.75;
// Redondea a entero inmediato
console.log(Math.ceil(num))
// --> 1

// Decimal
var num = 35.1;
// Redondea a entero inmediato
console.log(Math.ceil(num))
// --> 36

// Decimal
var num = 2.00000001;
// Redondea a entero inmediato
console.log(Math.ceil(num))
// --> 3

/* Incrementa el entero al mínimo decimal */
var num = -2.00000001;
// Redondea a entero inmediato
console.log(Math.ceil(num))
// --> -2

/* Negativos en cero redondean 
desde el mínimo decimal a 0 cero */
console.log(Math.ceil(-0.10));
// --> 0

Math.floor()

Redondea al entero inferior o igual del número dado más bajo aunque sea un decimal de 0.9

console.log(Math.floor(2.9999) );
// R. 2

console.log(Math.floor(10.9) );
// R. 10

console.log(Math.floor(0.9) );
// R. 0

Math.abs()

Devuelve el valor absoluto (sin signo)

console.log( Math.abs(-0.6));
// --> 0.6
console.log( Math.abs(-1.999999));
// --> 1.999999

Math.min() y Math.max()

Devuelve el entero o el decimal más grande -Math.max()- o el más bajo -Math.min()-. Permite números strings.

// Devuelve el número mayor
console.log(Math.max(9.6, "10",4));
/// R. 10 
/* Devuelve '10' aún siendo string. 
JS convierte internamente los strings 
literales en numéricos. */

// Devuelve el entero o decimal mayor
console.log(Math.max(0.6, "0.1",0.009999));
// R. 0.6

// Devuelve un entero o decimal más bajo
console.log(Math.min(1, 0.5, 2));
// R. 0.5

Math.sqrt()

Devuelve raíz cuadrada del argumento numérico dado.

console.log(Math.sqrt(-0)); // -0
console.log(Math.sqrt(2)); // 1.414213562373095
console.log(Math.sqrt(9)); // 3

console.log( Math.sqrt(2*3/100) );
// 0.2449489742783178

Math.cbrt()

Da la raíz cúbica de argumento numérico.

 // Raíz cúbica de (argumento)
// ¿Qué número, multiplicado 
// tres veces, da (el argumento)?
console.log(Math.cbrt(8));
// --> 2

Math.hypot()

Retorna la raíz cuadrada de la suma de dos valores –los dos lados de un triángulo rectángulo–, por ejemplo.

 var ladoA = 3, ladoB = 6;
console.log(Math.hypot(ladoA,ladoB));
// 6.708203932499369

/* Para calcular la distancia entre dos dimensiones */
console.log(Math.hypot(3, 4, 3));
// 5.830951894845301

Math.trunc()

Expresa un entero decimal quitando el decimal.

console.log(Math.trunc(3.2) );
// 3 

console.log(Math.trunc(-2.444) );
// 2
/*  Se mantiene negativo. 
Si dese desea quitar el negativo, 
entonces va Math.abs() */

var num = 3.4*3.98/9.908;
console.log(Math.trunc(num));
// 1

// Convertir a entero quitando el decimal
console.log(Math.trunc(-0.1122) );
// 0

Math.random()

Devuelve un número aleatorio decimal, entre 0 y 1.

Un ejercicio clásico es mostrar un número aleatorio según límites dados por un rango:

var aleat = Math.random();
// 0.283... o cualquiera

// Rango: Dame números desde 0 hasta el 100
var centenar = 100;

// Opción: Rango hasta el 1000
var miles = 1000;

// Multiplica el resultado random por el límite
var opera = centenar * aleat;

// Resultado con decimales. 
// Necesitamos redondear
console.log((opera));
// --> 34.85451320941382

// NÚMERO FINAL
// Redondea el resultado al entero más bajo
console.log( Math.floor(opera) );

Una estructura más simplificada para el mismo resultado sería el siguiente –donde el número sería el rango límite–.

/* Desde dentro hacia afuera, 
Math.random() da un número decimal entre 0 y 1. 
Luego, multiplica ese número por 10. 
Como sigue siendo decimal, Math.floor() 
lo convierte al entero inferior inmediado. */
console.log(Math.floor( Math.random() * 10) );
/* En cada actualización, 
se mostrará un nuevo valor */
/* 5...2...0....1 (...) */

Trabajar con argumentos y parámetros

Es muy posible que las explicaciones a continuación hayan sido necesarias mucho antes. Pero considero que es el momento adecuado –ya habiendo revisado algunos conceptos– para explicar cómo y porqué los argumentos y parámetros se comportan y cuáles son sus posibilidades en JS –y por ende, en cualquier lenguaje de programación-.

Para la Guía de Visual Basic de Microsoft existen diferencias entre parámetros y argumentos.

  • Parámetros: es el identificador –nombre– de un valor, que a su vez es contenido en sí mismo como una variable que se define en un contexto, como una función. Este parámetro podrá recibir un valor llamado argumento.
  • Argumento: es un valor/objeto –numérico, alfanumérico, literal– ingresado en el parámetro. Ese valor será llamando cuando el contexto –una función, por ejemplo- sea invocada. Los argumentos estan rodeados por paréntesis ().

Para la PC Magazine enciclopedia "los argumentos son valores independientes" que proveen de más datos a un contexto, como una función. Finalmente, es el usuario final quién decidirá qué valor podría ese argumento. La expresión 'pasar un argumento' significa que se ha alimentado con un valor a un parámetro.

¿Qué puede ser un argumento?

  • Un valor numérico, un string, booleano.
  • Una función
  • Una palabra clave, como this
  • Un método

En otras palabras, un parámetro es el identificador –o nombre– de una variable; y un argumento es el valor enviado al parámetro.

Similar a una bodega con dos grandes puertas que permiten ingresar objetos, las puertas de ingreso son los parámetros –con un letrero identificatorio que son los nombres de cada puerta–; mientras que los argumentos son los objetos ingresados externamente.

 // Se crea la función
function proceso(parametro1, parametro2) {

// Se pide devolver -cuando se requiera- los parámetros
return "Entraron " + parametro1 + " y " + parametro2;
}

// Se alimenta DE ARGUMENTOS los parámetros
console.log(proceso("objeto1", "objeto2"));
// Entraron objeto1 y objeto2

// * * * * * * * * 
// * * * * * * * * 
// * * * * * * * * 

// Se crea la función
function bodega(puerta1, puerta2) {

// Se pide devolver -cuando se requiera- los parámetros
return "Entraron " + puerta1 + " y " +puerta2;
}

// Se alimenta DE ARGUMENTOS los parámetros
console.log(bodega("silla", "escritorio"));
// Entraron silla y escritorio

FUNCIONES PREDEFINIDAS

Ya planteamos que las funciones son territorios delimitados cuyo papel es trabajar con datos desde un solo bloque de código; y nos permiten –mediante parámetros– insertar nueva información –en forma de argumentos–.

// parámetro 'lugar'
function turismo(lugar){

// Condicional
if (lugar == "cerro Santa Ana"){
console.log("El " + lugar + 
" se ubica en el centro de Guayaquil");

} else if (lugar == "Malecón 2000"){
console.log("El " + lugar + 
" se ubica frente al río Guayas");

} else {
console.log(lugar + " No está en Guayaquil");
}}

// Argumento 'Malecón 2000'
turismo("Malecón 2000");
/* --> El cerro Santa Ana 
se ubica en el centro de Guayaquil */

Como veremos, otras funciones ya predefinidas nos serán muy útiles.

parseInt()

A partir de un alfanumérico o dato string, detecta los primeros números y lo devuelve listos para ser mostrados. Si parseInt() no puede convertir los primeros números a enteros, retorna la propiedad NaN. Además de eso, parseInt() convierte un valor radix. Este determina el sistema numérico. Por ahora, solo mantendremos la conversión de alfanumérico a dato tipo número.

// parseInt()
var ingreso = '2023,–Guayaquil–';
// Solo muestra el primer conjunto de números
console.log(parseInt(ingreso));
// --> 2023

// ...y Sigue siendo string
console.log(typeof ingreso);

// No puede convertir. Envia NaN
console.log(parseInt("Edu2023"));

parseFloat()

Muy similar a parseInt(). parseFloat() busca el primer conjunto de decimales y los prepara para ser mostrados, obviando otros caracteres que no sean números.

// parseFloat()
var ingreso = '0.1416.–es PI';

// Solo muestra el primer conjunto de decimales
console.log(parseFloat(ingreso));
// 0.1416

// --> Sigue siendo string
console.log(typeof ingreso);
// string

console.log(parseFloat("a1234"));
// --> NaN

// También convierte exponentes
console.log(parseFloat('123e-2'));
// --> 1.23

isNaN()

NaN niega que un dato sea numérico y operable. isNaN() dará true si el dato evaluado es NaN –No número–; de otro modo –si NO es un número–, dará false.

 // El dato NO es número
console.log(isNaN("abc"));
// --> true

// lo indefinible no es número
console.log(isNaN(undefined));
// --> true

// Lo vacío es no es un no número
console.log(isNaN(null));
// --> false

// Negar que 200 es un número es erróneo. 
console.log(isNaN(200));
// --> false

// Negar la negación da una afirmación:
// 200 sí es un número 
console.log(!isNaN(200));
// --> true

eval()

Cuidado. Esta función es un hacha afilada. Bien usada, es útil para ejecutar líneas de código directamente desde JS cuando se hacen pruebas. Pero en manos equivocadas, es un peligro. SE RECOMIENDA EVITAR SU USO. Para más referencias, revisar aquí y aquí. Finalmente, en el artículo Eval is evil - Why we should not use eval in JavaScript, Amit Khonde detalla los problemas de desempeño y de seguridad que tiene esa función.

 /* Este podría ser un código malicioso */
eval(' var ojito = "Cuidado"; ');
console.log(ojito);
// --> Cuidado


Subir al inicio Seguir a la parte tercera