Una vez dominamos las bases de la programación y nuestro código va creciendo cada vez más, comprobaremos que las variables y funciones no suelen ser suficiente como para que nuestro código esté bien organizado y los mecanismos que tenemos a nuestro alcance quizás no resultan todo lo prácticos que deberían ser.
Tradicionalmente, Javascript no soportaba clases de forma nativa, pero en ECMAScript 2015
¿Qué es una Clase?
Como mencionamos en el apartado anterior, una clase sólo es una forma de organizar código de forma entendible con el objetivo de simplificar el funcionamiento de nuestro programa. Además, hay que tener en cuenta que las clases son «conceptos abstractos» de los que se pueden crear objetos de programación, cada uno con sus características concretas.
Esto puede ser complicado de entender con palabras, pero se ve muy claro con ejemplos:
En primer lugar tenemos la clase. La clase es el concepto abstracto de un objeto, mientras que el objeto es el elemento final que se basa en la clase. En la imagen anterior tenemos varios ejemplos:
En el primer ejemplo tenemos dos objetos:
pato
yratón
. Ambos son animales, por lo que son objetos que están basados en la claseAnimal
. Tantopato
comoratón
tienen las características que estarán definidas en la claseAnimal
: color, sonido que emiten, nombre, etc...En el segundo ejemplo tenemos dos objetos
seat
yopel
. Se trata de dos coches, que son vehículos, puesto que están basados en la claseVehículo
. Cada uno tendrá las características de su clase: color del vehículo, número de ruedas, marca, modelo, etc...En el tercer ejemplo tenemos dos objetos
cuadrado
yc2
. Se trata de dos formas geométricas, que al igual que los ejemplos anteriores tendrán sus propias características, como por ejemplo el tamaño de sus lados. El elementocuadrado
puede tener un lado de3
cm y el elementoc2
puede tener un lado de6
cm.
Instanciar una clase
Se le llama instanciar una clase, crear un objeto o crear una instancia a la acción de crear un nuevo objeto basado en una clase particular. Esta acción la realizamos a través de la palabra clave new
, seguida del nombre de la clase, la cuál puede tener parámetros, en cuyo caso se controlarían desde un constructor
, concepto que veremos más adelante.
En Javascript, para instancia una clase, se utiliza una sintaxis muy similar a otros lenguajes como, por ejemplo, Java. Es tan sencillo como escribir lo siguiente:
// Declaración de una clase (de momento, vacía)
class Animal {}
// Crear (instanciar) un objeto basada en una clase
const pato = new Animal();
El nombre elegido debería hacer referencia a la información que va a contener dicha clase. Piensa que el objetivo de las clases es almacenar en ella todo lo que tenga relación (en este ejemplo, con los animales). Si te fijas, es lo que venimos haciendo hasta ahora con objetos como
Observa que luego creamos una variable donde hacemos un new Animal()
. Estamos creando un objeto pato
que es de tipo Animal
, y que contendrá todas las características definidas dentro de la clase Animal
(de momento, vacía).
Una norma de estilo en el mundo de la programación es que las clases deben siempre empezar en mayúsculas (nomenclatura llamada PascalCase). Esto nos ayudará a diferenciarlas sólo con leerlas. Si te interesa este tema, puedes echar un vistazo al tema de las convenciones de nombres en programación.
Miembros de una clase
Una clase tiene diferentes características que la forman, que generalmente se denominan miembros, y que normalmente son de dos tipos: propiedades y métodos. Vamos a ir explicándolas detalladamente. Pero primero, una tabla general para verlas en conjunto, con sus tipos:
Elemento | Descripción | Más información |
---|---|---|
Propiedad | Variable que existe dentro de una clase. Puede ser pública o privada. | Ver propiedades |
Propiedad pública | Propiedad a la que se puede acceder desde fuera de la clase. | |
Propiedad privada | Propiedad a la que no se puede acceder desde fuera de la clase. | |
Propiedad computada | Función para acceder a una propiedad con modificaciones (getter/setter). | |
Método | Función que existe dentro de una clase. Puede ser pública o privada. | Ver métodos |
Método público | Método que se puede ejecutar desde dentro y fuera de la clase. | |
Método privado | Método que sólo se puede ejecutar desde dentro de la clase. | |
Constructor | Método especial que se ejecuta automáticamente cuando se crea una instancia. | |
Método estático | Método que se ejecuta directamente desde la clase, no desde la instancia. | |
Inicializador estático | Bloque de código que se ejecuta al definir la clase, sin necesidad de instancia. |
Como vemos, todos estas características se dividen en dos grupos:
- Las propiedades: a grandes rasgos, variables dentro de clases
- Los métodos: a grandes rasgos, funciones dentro de clases
Un ejemplo sencillo de cada uno:
class Animal {
// Propiedades
name = "Garfield";
type = "cat";
// Métodos
hablar() {
return "Odio los lunes."
}
}
En los siguientes capítulos veremos que son exactamente, como se utilizan y que tipos de propiedades y métodos diferentes existen dentro del mundo de la programación orientada a objetos y sus clases.
La palabra clave this
Más adelante utilizaremos mucho la palabra clave this
. Esta es una palabra clave que se utiliza mucho dentro de las clases para hacer referencia al objeto instanciado. Ojo, que hace referencia al objeto instanciado y no a la clase:
class Animal {
name; // Propiedad (variable de clase sin valor definido)
constructor(name) {
this.name = name; // Hacemos referencia a la propiedad name del objeto instanciado
}
}
Observa que la palabra clave this
no se refiere a la clase Animal
exactamente, sino a la variable que utilizamos al instanciarla. Es decir, si hacemos un const pato = new Animal()
, se ejecutaría el constructor y la palabra clave this
haría referencia a pato
, por lo que this.name
estaría haciendo referencia a pato.name
.
Es importante tener mucho cuidado con la palabra clave this
, ya que en muchas situaciones creemos que devuelve una referencia al elemento padre que la contiene, pero en su lugar, devolverá el objeto Window
, ya que se encuentra fuera de una clase o dentro de una función con otro contexto:
function hello() {
return this;
}
hello(); // Window
const object = { hello } // Metemos la función dentro del objeto
object.hello() === object; // true
En este caso, podemos ver que si ejecutamos la función hello()
en un contexto global, nos devuelve el padre, es decir, el objeto Window
. Sin embargo, si metemos la función hello()
dentro de un objeto object.hello()
nos devuelve el padre, es decir, el propio objeto object
.
Ten cuidado al utilizar
this
. Asegúrate siempre de quethis
tiene el valor que realmente crees que tiene.
Buenas prácticas
Veamos una serie de buenas prácticas a la hora de trabajar con clases, antes de profundizar en sus características.
Clases en ficheros externos
Generalmente, para tener el código lo más organizado posible, las clases se suelen almacenar en ficheros individuales, de forma que cada clase que creamos, debería estar en un fichero con su mismo nombre:
// Animal.js
export class Animal {
/* Contenido de la clase */
}
Luego, si queremos crear objetos basados en esta clase, lo habitual suele ser importar el fichero de la clase en cuestión y crear el objeto a partir de la clase. Algo similar al siguiente fragmento de código:
// index.js
import { Animal } from "./Animal.js";
const pato = new Animal();
Si nuestra aplicación se complica mucho, podríamos comenzar a crear carpetas para organizar mejor aún nuestros ficheros de clases, y por ejemplo, tener la clase Animal.js
dentro de una carpeta classes
(o algo similar). Esto nos brindaría una mejor experiencia de desarrollo, pero el nombre de las carpetas o su organización ya dependería del desarrollador o del equipo de desarrollo.
Nombrado de miembros
En los próximos capítulos ya profundizaremos en las propiedades y los métodos de una clase, pero una buena práctica para no confundirnos a la hora de utilizarlos, es a la hora de ponerles nombres. Es muy aconsejable que las propiedades de una clase tengan nombre de sustantivos (son elementos, valores, ítems...), mientras que los métodos de una clase deberían tener nombre de verbos (son acciones, operaciones, etc...).
Otro buen consejo, mucho más general, es que intentes escribir código en inglés, ya que eso hará que el código que hagas sea mucho más universal, y sea más fácil de modificar por otras personas.
Clases con pocas líneas
Otro consejo interesante a la hora de trabajar con clases sería intentar que las clases se mantengan pequeñas, con pocas líneas de código. El número de líneas de código ideal es difícil de saber, pero un buen número, por ejemplo, podría ser entre 100-500 líneas de código, como menciona la regla max-lines de la herramienta de revisión de código ESLint.
Si descuidamos la cantidad de líneas de código por fichero al programar, es muy probable que con el tiempo la clase (o el fichero) vaya creciendo en líneas y se vuelva muy dificil de mantener y modificar.
Para evitar esto, lo ideal siempre es mantener, siempre que sea posible, un número bajo de líneas de código, y si la clase se está haciendo muy grande, intentar dividirla en varias clases. Es decir, buscar una serie de criterios para poder refactorizar y separar ciertos detalles en una nueva clase:
Personaje.js Personaje.js Vida.js
------------- ------------- --------
- Vidas restantes (propiedad) - Vida (propiedad) --> - Vidas restantes (propiedad)
- Fuerza (propiedad) - Fuerza (propiedad) - Energía (propiedad)
- Energía (propiedad) - Hablar (método) - Curar (método)
- Velocidad (propiedad) - Disparar (método)
- Hablar (método)
- Disparar (método)
- Curar (método)
En este ejemplo, hemos separado en una nueva clase Vida
en el fichero Vida.js
, los conceptos Vidas restantes
, Energía
y Curar
ya que tienen relación entre sí (hacen referencia a la vida del personaje), de modo que ahora en la clase Personaje
simplemente tenemos una propiedad que hace referencia a un objeto de esa clase Vida
, con sus valores particulares.
Esto hará que, si el fichero Personaje.js
ocupaba demasiadas líneas, consigamos reducirlas, puesto que hemos movido parte de su código a otro fichero, y de paso hemos mejorado mucho nuestro código, ya que ahora está separado en temas más específicos, que son más pequeños y más fáciles de controlar.