Clases en JavaScript
En el último artículo, se introdujeron algunos de los conceptos básicos de la programación orientada a objetos (POO), y discutimos un ejemplo donde usamos principios de la POO para modelar los profesores y alumnos de una escuela.
También hablamos acerca de cómo podemos usar prototipos y constructores para implementar un modelo como éste, además, vimos que Javascript también proporciona características que se asemejan más a los conceptos de la POO clásica.
Prerequisitos: | Conocimientos básicos de informática, comprensión básica de HTML y CSS, familiaridad con conceptos básicos de Javascript (mira Primeros pasos y Construyendo con bloques) y lo esencial de JSOO (Javascript orientado a objetos)(mira Introducción a los objetos y Programación orientada a objetos) |
---|---|
Objetivo: | Comprender como utilizar las características que Javascript proporciona para implementar aplicaciones usando programación orientada a objetos "clásica". |
Clases y constructores
Puedes utilizar la palabra clave class
para declarar una clase. A continuación se muestra la declaración de la clase Persona
de nuestro artículo anterior.
class Person {
name;
constructor(name) {
this.name = name;
}
introduceSelf() {
console.log(`¡Hola!, soy ${this.name}`);
}
}
Esto declara una clase llamada Person
con:
- una propiedad
name
. - un constructor que recibe un parámetro
name
que se usa para inicializar la propiedadname
del nuevo objeto. - un método
introduceSelf()
que puede hacer referencia a las propiedades del objeto usandothis
.
La declaración name;
es opcional y puedes omitirla, ya que la línea this.name = name;
en el constructor crea la propiedad name
antes de inicializarla. En cualquier caso, enlistar las propiedades de manera explícita en la declaración de la clase hará que sea más fácil para las personas leyendo tu código saber cuáles son las propiedades que conforman a dicha clase.
Cuando declaras una propiedad, también puedes inicializarla con un valor por defecto con una línea como name = '';
.
El constructor se define utilizando la palabra clave constructor
. Al igual que un constructor fuera de una clase, se encargará de:
- crear un nuevo objeto
- asociar
this
al nuevo objeto, para que puedas hacer referencia athis
dentro de tu código en el constructor - ejecutar el código en el constructor
- retornar el objeto recién creado.
Dada la declaración de clase anterior, puedes crear y utilizar una nueva instancia de Person
de la siguiente manera:
const gil = new Person("Gil");
gil.introduceSelf(); // ¡Hola!, soy Gil
Observa que para llamar al constructor usamos el nombre de la clase, Person
en este ejemplo.
Omitiendo el constructor
En caso de que no necesites algún tipo de inicialización, puedes omitir al constructor, en este caso un constructor por defecto es generado.
class Animal {
sleep() {
console.log("zzzzzzz");
}
}
const spot = new Animal();
spot.sleep(); // 'zzzzzzz'
Herencia
A partir de la clase Person
, vamos a definir la subclase Professor
.
class Professor extends Person {
teaches;
constructor(name, teaches) {
super(name);
this.teaches = teaches;
}
introduceSelf() {
console.log(
`Mi nombre es ${this.name}, yo seré tu profesor de ${this.teaches}`,
);
}
grade(paper) {
const grade = Math.floor(Math.random() * (5 - 1) + 1);
console.log(grade);
}
}
Para expresar que una clase hereda de otra clase, utilizamos la palabra clave extends
.
Debido a que la clase Professor
agrega una nueva propiedad teaches
, ésta se declara en el cuerpo de la clase.
Cuando creamos un nuevo Professor
, queremos establecer el nombre de teaches
que enseña, para esto definimos un constructor que recibe name
y teaches
como argumentos. Lo primero que hace el constructor es utilizar super()
para llamar al constructor de la superclase, mientras le pasa el parámetro name
. El constructor de la superclase se encarga de establecer el valor de la propiedad name
. Enseguida, el constructor de la clase Professor
establece el valor de la propiedad teaches
.
Nota: Si una subclase debe realizar algún tipo de inialización, es obligatorio que esta llame al constructor de la superclase utilizando super()
con los paramétros que el constructor de la superclase espera.
Podemos observar que hemos sobreescrito el método introduceSelf()
de la superclase y a su vez agregamos un nuevo método grade()
para calificar las tareas de los alumnos (Nuestro profesor no es muy bueno que digamos, solo asigna calificaciones aleatorias a los ensayos).
Con esta declaración ahora podemos crear y usar profesores:
const walsh = new Professor("Walsh", "Psicología");
walsh.introduceSelf(); // 'Mi nombre es Walsh, yo seré tu profesor de Psicología'
walsh.grade("mi tarea"); // una calificación aleatoria
Encapsulamiento
Finalmente, veamos cómo implementar el encapsulamiento en JavaScript. En el anterior artículo hablamos de cómo nos gustaría que la propiedad year
de Student
fuera privada, de esta forma podríamos cambiar las reglas sobre la clase de tiro con arco sin estropear el código que depende de la clase Student
.
Aquí podemos ver la declaración de la clase Student
que se encarga precisamente de ello:
class Student extends Person {
#year;
constructor(name, year) {
super(name);
this.#year = year;
}
introduceSelf() {
console.log(
`¡Hola! me llamo ${this.name} y estoy en el año ${this.#year}.`,
);
}
canStudyArchery() {
return this.#year > 1;
}
}
En la clase anterior, year
es una propiedad de dato privada. Podemos crear un objeto Student
que puede acceder a la propiedad #year
internamente, sin embargo, si algún código que se encuentre afuera de la clase intenta acceder a la propiedad #year
, el navegador lanzará un error:
const summers = new Student("Summers", 2);
summers.introduceSelf(); // ¡Hola! me llamo Summers y estoy en el año 2.
summers.canStudyArchery(); // true
summers.#year; // SyntaxError (Error de sintaxis)
Las propiedades de datos privadas deben ser declaradas en la propia declaración de la clase y sus nombres deben empezar con #
.
Métodos privados
En un clase puedes incluir tanto propiedades de dato privadas como métodos privados. Al igual que las propiedades de dato privadas, su nombre debe empezar con #
y solo pueden ser llamados por los propios métodos del objeto:
class Example {
somePublicMethod() {
this.#somePrivateMethod();
}
#somePrivateMethod() {
console.log("¿Me llamaste?");
}
}
const myExample = new Eaxmple();
myExample.somePublicMethod(); // '¿Me llamaste?'
myExample.#somePrivateMethod(); // SyntaxError (Error de Sintaxis)
¡Pon a prueba tus habilidades!
Haz llegado al final de este artículo pero, ¿Aún recuerdas la información más importante? Puedes encontrar retos para asegurarte de retener esta información antes de continuar - véase Pon aprueba tus habilidades: Javascript orientado a objetos.
Resumen
En este artículo repasamos las herramientas principales que existen en JavaScript para crear programas orientados a objetos. A pesar de que no abarcamos este tema completamente, lo que vimos debería ser suficiente para iniciar. Nuestro artículo sobre clases es un buen lugar para aprender más.