duduromeroa.com

#Javascript, #DOMJs, #métodos, #propiedades, #valores, #Guayaquil

Programación para web con JS (5/8): clases, propiedades, valores, métodos, protección de valores


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

Por Eduardo J. Romero Andrade
| Guayaquil

Contacto duduromeroa@gmail.com




SIMULAR EL MUNDO REAL MEDIANTE CLASES, PROPIEDADES Y VALORES

Hoy en día, lenguajes como Javascript intentan que un sistema de cómputo interprete el mundo como un gran conjunto de clases y objetos: Clases, como creadoras de propiedades; y objetos, como generadores de nuevos valores. Esta sección explica la relación entre ellos.

En criterios de programación -también llamados paradigmas- una clase es un conjunto de propiedades y valores desde el cual 'nacen' objetos. La clase 'humanos' contiene propiedades que la diferencian de otras clases de mamíferos. A partir de 'humanos' surge una diversidad de razas y etnias. Es decir, cada 'cosa' derivada de la clase se apropiará o heredará las características de la clase madre. El modelo de programación orientado a objetos, explicado en otra sección, fue la base para ese paradigma.

Para Flanagan[3], capítulo IX, los objetos en programación son entidades con propiedades similares pero valores únicos. Similar al patrón creado por una hoja de papel para armar un pequeño avión. La clase sería el diseño del avión en el papel, el objeto sería cada avión armado según ese diseño; ya la propiedad color podría variar de avión en avión. Unos serían amarillos, otros de líneas rojas, etcétera.

Todo eso ahorra trabajo al usar 'moldes' desde donde se crean objetos que heredan algo o mucho de su clase madre; y evita duplicar cada gran conjunto de propiedades en cada nuevo objeto. Es decir, ya sean uno o cien aviones de papel, cada avioncito creado a punta de doblar el mismo patrón 'clase' heredará propiedades –forma y color, por ejemplo– a los que luego se le añadirán nuevos valores –un color diferente o una variación a la forma-.

 class NuevaClase {
...(propiedadA, propiedadB){
propiedadA = valor;
propiedadA = valor;
}}

/* Cada nuevo objeto agregará NUEVOS 
VALORES a las propiedades heredadas */
... objetoA = ... (nuevoValor, nuevoValor);
... objetoB = ... (nuevoValor, nuevoValor);

Como veremos, Javascript usa un acercamiento diferente al darle más importancia a los objetos que a las clases; en comparación con otros lenguajes de programación, como Java o C#, en donde las clases gozan de mayor importancia en estructura y sintaxis.

CLASES Y OBJETOS

Para Svekis y Putten[5], las clases en JS son funciones madre con propiedades y sus valores. También, una clase es un conjunto de objetos con propiedades similares. Una palabra clave y un método permiten concebir una clase: la clave class y el método constructor. Y luego, el operador new permite que nazcan objetos desde esa clase.

PALABRA CLAVE CLASS

Para mdn web docs la clave class es una declaración creadora de objetos.

MÉTODOS

Para Svekis y Putten[5] los métodos son funciones dentro de las clases. Y cuando se los definen no es permitido anteceder a los métodos con la clave function. Simplemente se usa un identificador y luego, al invocarlo, se lo hace con el nombre del método.

MÉTODO CONSTRUCTOR

Para mdn web docs el método constructor crea e inicializa las propiedades y métodos de un objeto; y asigna nuevos valores a esas propiedades. Para Svekis y Putten[5], el constructor es una única función dentro de una clase que permite crear instancias o 'nacimientos' de objetos a partir de la clase madre. Así como ocurre en las funciones, un constructor es llamado o invocado al crear un objeto mediante la palabra clave new:

PALABRA CLAVE THIS

La clave this apunta al contexto más inmediato en que se ejecuta. Ese contexto puede ser una función o un objeto. Recordemos pero un método es una función que trabaja desde dentro de un objeto.

Para autores desde canales de YouTube como midulive y Developeando, cuando 'this' está siendo usado desde un método de objeto, entonces 'this' siempre apuntará ese objeto; o si está explícito, apuntará a una de las propiedades de ese objeto.

En Javascript

/* OBJETO CON MÉTODOS */
const guayas = {
	ciudad: "Guayaquil",
	sitio: "Costa",

	apuntaObjeto(){
	// Apunta al objeto dueño de esta función
	console.log(this);
	},

	// Métoodo con parámetro
	cambiar(ciudad){
	// Apunta a propiedad del objeto dueño de esta función
	console.log(this.ciudad);
	// Muestra argumento de funcion cambiar()
	console.log(ciudad);
	}
}

	// INVOCA DESDE MÉTODO: console.log(this);

// Se invoca objeto + método apuntaObjeto()
guayas.apuntaObjeto();
// Muestra objeto completo 
/* -> {ciudad: 'guayaquil', sitio: 'costa', apuntaObjeto: [Function: apuntaObjeto], cambiar */

// Se invoca objeto + método cambiar(ciudad)
guayas.cambiar("Quito");
// Muestra argumento en método cambia()
/* -> guayaquil Holi */

En el ejemplo de abajo, tan solo la invocación de this desde fuera de alguna función o de un objeto apunta finalmente al [object Window] más inmediato contenido en la ventana del navegador desde donde se ejecuta el código.

// HTML elemento solo para mostrar mensaje
<p class="mostrandoMsn"> </p>

// JS 
// Se apunta elemento HTML para mostrar mensaje
var mostrarMessage = document.querySelector(".mostrandoMsn");
// Únicamente se está invocando 'this'
mostrarMessage.innerHTML = this;
// [object Window]

Abajo, otro comportamiento de 'this' es el de apuntar al dato de un parámetro cuando se aplica desde una función constructor.

En Javascript

function calculo(a, b) {
// Apunta parámetros
  this.a = a;
  this.b = b;
}

// Se crea nuevo objeto con nuevos argumentos
const nuevo = new calculo(100, 200);
// Se invoca uno de los argumentos
console.log(nuevo.a);
// 100

Abajo, otro comportamiento de 'this' es apuntar a las propiedades desde un método de objeto. El método de objeto (en forma de una función con propiedades y valores) contiene acciones que se ejecutarán cuando se invoque el objeto, operando también las propiedades y valores del método. Sin embargo, también podemos apuntar a los valores del objeto mediante la sintaxis nombreObjeto . propiedad.

En Javascript

var humano = {
nombre: "Edu",
edad: 45,
estudios: "ingeniería",
	resumen: function() {
	console.log(
	"El humano " + humano.nombre + 
	" con edad " + this.edad + 
	" estudió " + this.estudios );
	}
};

// Se invoca objeto y función anidada
humano.resumen();
/* El humano Edu con edad 45 estudió ingeniería */

Abajo, otro ejemplo similar para devolver los datos desde 'return' dentro del método del objeto. Si bien se usó this, también se usó la sintaxis nombreObjeto.propiedad. Ambas funcionan para el mismo objetivo.

En Javascript

const opSecreta = {
	valor: 100,
	prop: 42,
	resultado: function () {
	return this.prop * opSecreta.valor;
  },
};
console.log(opSecreta.resultado());
// 4200

Abajo, en este nuevo ejemplo la clave 'this' permite heredar y obtener los valores de las propiedades del método de un objetoPadre hacia un objetoHijo. De igual manera, se ensayó dos formas de acceder a las propiedades: mediante la sintaxis nombreObjeto.nombrePropiedad; y mediante this.nombrePropiedad. Ambas funcionan.

En Javascript

// Crea objeto y método-función
var objetoPadre = {
  cuentasDBanco: function () {

/* Los argumentos 'a' y 'b' son
propiedades vacías, pero se espera 
que algún día, cuando esas propiedades
y su función sean heredadas por un nuevo objeto,
este las alimente con valores. */
    return objetoHijo.a + this.b;
  }
};

/* OJO con esta herencia: 
'objetoHijo' hereda las propiedades 
de 'objetoPadre'. Ahora sí, las propiedades 
'a' y 'b' serán alimentadas con datos */
var objetoHijo = Object.create(objetoPadre);

// Alimento con valores las propiedades
// Se las invoca desde el 'objetoHijo'
objetoHijo.a = 100;
objetoHijo.b = 200;

// Se invoca el NUEVO objeto
console.log("Total " + objetoHijo.cuentasDBanco());
// 300

Gráfico que explica cómo la palabra clave this apunta a propiedades que solo serán alimentadas con valores cuando las herede un objeto instanciado desde el objeto padre. duduromeroa.com

Otra aplicación de this es captar el contexto de una función desde un elemento HTML; es decir, desde fuera de JS. En el ejemplo de abajo, this ayuda a captar el contenido textual del elemento para luego ser operado desde JS. Sin embargo, ese mismo código puede ser armado desde una vinculación (más correcta) mediante addEventListener apuntando hacia el elemento HTML y evitar así el uso de 'this':

Dar clic porfi

En HTML y JS

//HTML
<div class="cosoHtml" onclick="mostrarTxxt(this)">Dar clici porfi</div>

// Javascript
/* 
- Se activa la función
- Se se extrae el contenido textual 
del elemento gracias a this */
function mostrarTxxt(cosito) {
const itemText = cosito.textContent;
alert(`Hiciste clic en : ${itemText}`);
}

Finalmente, se menciona un ejemplo de 'this' en una función invocada desde un eventListener. En este caso, 'this' apunta al elemento activador porque en este contexto, solo queda esa posibilidad (un elemento que dispara un evento). Es por eso que el contexto en el cual 'this' es usado importa.

En Javascript

var elemActivador = document.querySelector(".elementoInterac");

function pintarDAzul1() { 
// con 'this'
  this.style.backgroundColor = "orange"; 
  this.innerHTML = "Clic dado";
}

function pintarGris() {
// con nombreElemento
  elemActivador.style.backgroundColor = "silver";
  elemActivador.innerHTML = "Da clic";
}

// Ojo como se cambia el evento para invocar una nueva función
// mousedown
elemActivador.addEventListener("mousedown", pintarDAzul1, false);
// mouseup
elemActivador.addEventListener("mouseup", pintarGris, false);

/* La función no está con '()', puesto 
que solo se requiere el identificador de la función */

Da clic


OPERADOR NEW

Con el operador new ocurre un llamado al constuctor y luego un nacimiento de un objeto. Es decir, luego de la invocación se ejecuta una copia de propiedades desde la clase madre hacia el nuevo objeto pero con nuevos valores agregados en forma de argumentos. En el código siguiente, cada nuevo hijo –un objeto– tendrá dos propiedades heredadas de la clase madre: nombre y color de cabello. Pero en cada nacimiento o instancia, cada propiedad tendrá nuevo valor.

En el siguiente ejemplo se muestra cómo los elementos ya explicados se unen para crear clases. Por acuerdo común, los nombres de las clases inician con mayúscula:

// -> Función con declaración de clase
class Madre{
/* Método que construye la clase y le 
da parámetros en forma de propiedades */
constructor(nombre, colorCabello){
// -> PROPIEDADES CON VALORES
this.nombre = nombre;
this.colorCabello = colorCabello;
}
// MÉTODO de la clase Madre
mensaje(){
console.log(primeroHijo.nombre + " es hermoso.");
}

}
// -> NACEN Objetos de la clase Madre
// -> Propiedad ------------|Nombre---|Cabello
let primeroHijo = new Madre("Julito", "rubio");
let segundoHijo = new Madre("Anita", "negro");

// -> Invocar un hijo de la clase Madre
console.log("El primer hijo se llama " + primeroHijo.nombre + 
" y es " + primeroHijo.colorCabello);
// -> El primer hijo se llama Julito y es rubio

// Se invoca nombre de objeto y nombre de función de clase
primeroHijo.mensaje();
// -> Julito es hermoso.
  • Clases creadas con la declaración class deben ser invocadas –para instanciar objetos– con el operador new.
  • Las funciones arrow '=>' no permiten definir constructores.

Por otro lado, en el ejemplo de abajo se crea una función 'patronAvion' con tres parámetros. Ya en el cuerpo de la función cada parámetro será una propiedad -pliegue, color, etc.– que representa a su propio identificador pero antecedido con la clave this. Luego se crean nuevas variables cuyo valor es un nuevo –new– objeto nacido de la función madre pero con nuevos valores. Finalmente, la invocación al identificador de cada variable mostrará a cada objeto con iguales propiedades, pero con características –valores– nuevos.

 
// Función padre, con parámetros
function patronAvion(pliegues, color, dimension){
/* Parámetros y propiedades tienen el mismo nombre. */
this.pliegues = pliegues;
this.color = color;
this.dimension = dimension;
}

// Nuevos objetos se crean con el operador constructor 'new'
// Nuevos argumentos inicializarán nuevas propiedades
let avionAA = new patronAvion("4", "rojo", "mediano"); 
let avionBB = new patronAvion("6", "blanco", "pequeño"); 

// Invocar nuevo objeto hijo
console.table(avionAA);
/* 
Se heredaron propiedades, 
pero sus valores son diferentes
┌───────────┬───────────┐
│  (index)  │  Values   │
├───────────┼───────────┤
│ pliegues  │    '4'    │
│   color   │  'rojo'   │
│ dimension │ 'mediano' │
└───────────┴───────────┘
*/

// Invocar nuevo objeto hijo
console.table(avionBB);
/* 
Se heredaron propiedades, 
pero sus valores son diferentes
┌───────────┬───────────┐
│  (index)  │  Values   │
├───────────┼───────────┤
│ pliegues  │    '6'    │
│   color   │ 'blanco'  │
│ dimension │ 'pequeño' │
└───────────┴───────────┘
*/

En otro ejemplo, una clase contiene dentro una función cuyo parámetro puede ser alimentado desde fuera al ser instanciado el objeto creado:

 // 
class Mascota{
	/* Se construye clase con 
	propiedades de la clase */
	constructor(nombre, tipo){
	this.nombre = nombre;
	this.tipo = tipo;
	}
		// Una función interna con parámetro
		vacunas(vacunado){
		// Condicional de parámetro
			if (vacunado == 1) 
			{return "SÍ tiene vacuna";} 
			else {return "NO tiene vacuna"};
			}
}

//  CREAR OBJETOS 
/* Instacia 'perro' */
let perro = new Mascota("Nacho", "perro");

/* Instacia 'loro' */
let loro = new Mascota("Guille", "loro");

// INVOCAR Y MOSTRAR 
// De 'perro' 
console.log("-- Mi " + perro.tipo + " de nombre " + 
perro.nombre + " " + perro.vacunas() );
// -> -- Mi perro de nombre Nacho NO tiene vacuna

// De 'loro' 
console.log("-- Mi " + loro.tipo + " de nombre " + 
loro.nombre + " " + loro.vacunas(1) );
// -> -- Mi loro de nombre Guille SÍ tiene vacuna

PROTEGER LOS VALORES DE LAS PROPIEDADES

Se antecede el signo '#' a cada propiedad de la clase para prohibir su acceso desde fuera de las clase padre. Es decir, solo se podrá acceder –y pedir mostrar– los valores de esas propiedades únicamente desde dentro de la función. Con esa acción de bloqueo se concreta la encapsulación o protección de los datos desde una clase, una característica de la programación orientada a objetos

.

 class Calificar {
// Propiedades protegidas
#semestreA; 
#semestreB;

constructor(semestreA, semestreB){
this.#semestreA = semestreA;
this.#semestreB = semestreB;

// Desde dentro se puede ACCEDER y MOSTRAR
console.log("Desde dentro -> Semestre A: " + semestreA);
// --> Desde dentro de la función -> Semestre A: 9.5
}}

/* Nuevo objeto creado y  
nuevos valores de propiedades. 
Pero esos valores NO PUEDEN accederse 
desde fuera de la clase Calificar. */
let estudiante = new Calificar(9, 10);
console.log(estudiante.semestreA);
// -> Undefined

Por otro lado, en el ejemplo de abajo se define un nombre identificador de un clase para calcular un número. También se inicializa dentro de la clase una propiedad que guarda un resultado para luego ser invocado:

 var EdadMascota = class {constructor(nace, hoy) 
{ this.edad = nace - hoy; } 
};

// Objeto > conocer la edad de Nico
let edadNico = new EdadMascota(2022, 2017).edad;

// Objeto > conocer la edad de Lilita
let edadLilita = new EdadMascota(2022, 2015).edad;

// Invocar edadNico
console.log("Nico cumple " + edadNico + " años de edad.");
// -> Nico cumple 5 años de edad.

// Invocar edadLilita
console.log("Lilita cumple " + edadLilita + " años de edad.");
// -> Lilita cumple 7 años de edad.

Anidando arreglos y objetos entre sí

Arreglos y objetos pueden anidarse entre sí. Incluso podemos insertar nuevas propiedades desde una función externa. Para Flanagan[3], 'cada objeto puede ser extendido con nuevas propiedades y métodos'.

Por otro lado, aquí el uso de la clave this es esencial. Para Flanagan[3] (capítulo 1.3 | A tour of Javascript), this 'siempre apuntará al objeto en el cual el método esta siendo activado':

En Javascript

// Arreglo con objeto anidado
let coord = [
{ x: 0, y: 0 },
{ x: 1, y: 1 }
];

// Se agrega nueva propiedad '.dist' al objeto coord
/* El método que se agrega calculará las distancias entre los puntos */
coord.dist = function(){
// 'This' APUNTA a cada objeto dentro del arreglo
let p1 = this[0];
let p2 = this[1];
// Luego, podemos acceder a sus valores
let a = p2.x - p1.x;
let b = p2.y - p1.y;

return Math.sqrt(a*a + b*b);
}

// Invocamos el MÉTODO contenido en el nuevo objeto 'dist'
console.log(coord.dist());
// 1.4142135623730951
xxx.
Sin la clave this, no podríamos acceder a los valores de los objetos contenidos en el objeto. Elaboración propia.

CREAR CLASE CON UN MÉTODO PARA INSERTAR REGLAS CSS DESDE JAVASCRIPT

Combinación de uso de clases, métodos, junto con innerHTML, document.createElement, document.createElement y appendChild()
  • Las acciones generales serán:
  • Crear clase
  • Insertarle método para crear elemento HTML
  • Crear regla CSS
  • Adjuntarlo regla CSS mediante método de ingreso de caracteres (ver abajo diferencia entre innerHTML y textContent )
  • Instanciar objeto que contiene esa regla
  • Invocar método para insertar regla CSS desde Javascript
  • Atención con el uso de innerHTML vs textContent para insertar caracteres en una página web
  • innerHTML usa, interpreta y respeta cualquier caracter que deseemos insertar: desde espacios, signos de cierre y apertura de código, comillas, puntos y comas, barras. Al procesarlos y mostrarlos obviará las etiquetas.

    En Javascript

    /* Mostrará 'Duduuuuuu!' en rojo y en la página */
    document.body.innerHTML = 
    "<h1 style='color:red;'>Duduuuuuu!</h1>";
    

  • textContent no interpreta las etiquetas y caracteres que insertemos. Solo los entiende como si fuera texto común.
  • En Javascript

    /* Mostrará todo el texto, tal cual esta */
    document.body.textContent = 
    "<h1 style='color:red;'>Duduuuuuu!</h1>";
    
    ATENCIÓN Es necesario entender esas diferencias cuando agreguemos campos para que el usuario ingrese texto. Pueden usarse para código dañíno.

  • Tanto innerHTML como textContent funcionarán en el ejemplo de abajo para insertar desde el método una regla CSS, y desde el propio código interno, no desde un campo de texto de usuario.

El siguiente ejemplo (que está ya procesado por el navegador -el código usado se muestra debajo-) usa una clase que contiene: un generador de nuevos elementos HTML, y ese mismo elemento contendrá una regla CSS que luego será adjuntada al elemento <head> para que esa regla CSS esté activa, pero agregada desde Javascript.

Es decir, sin la clase creada desde Javascript y que adjunta esa regla CSS, el texto de abajo no tendría esos estilos. Adjunto el script abajo:

Texto con CSS insertado desde JS

En HTML

<div class="coso">Esto tendrá una regla CSS</div>

En Javascript

class Estilosx {
// Método
      colorearCSS() {
  // Aloja un elemento STYLE
          const contenedorReglaCSS = document.createElement('style');

  // Alojador accede a método
  // Y entre comillas simples...
  // La regla CSS
          contenedorReglaCSS.innerHTML = `
              .coso {
                  color: red;
                  padding: 1rem;
                  text-align: center;
                  font-size: 2.3rem;
                  font-family: arial;
              }
          `;
  // Aún seguimos dentro de la función
  // Conectamos elemento head, adjuntamos 
          document.head.appendChild(contenedorReglaCSS);
      }
}

// Derivamos un objeto desde la clase Estilos()
const estilosInstance = new Estilosx();

// Conectamos OBJETO con la función interna colorearCSS()
estilosInstance.colorearCSS();
// Estilo ya está insertado en el head de la página


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.
    Editorial Packt.
    Information and Computation. Elsevier.
    ACM SIGCSE BulletinVolume 19Issue 1Feb.