Skip to content

¿Que es una clase?

Una clase es una plantilla para crear objetos, que contiene los atributos y métodos que van a tener los objetos creados a partir de ella. Tan sólo define la estructura de los objetos, pero no contiene datos concretos, estos datos se definen al instanciar una clase.

Anteriormente hicimos una funcion factoria para crear objetos de tipo Persona:

javascript
function crearPersona(nombre, apellido, edad) {
  return {
    nombre,
    apellido,
    edad,
    saludo() {
      return `${this.nombre} ${this.apellido}`
    },
    esMayorDeEdad() {
      return this.edad >= 18
    }
  }
}

const persona1 = crearPersona("Juan", "Perez", 20)
const persona2 = crearPersona("Maria", "Gomez", 15)
function crearPersona(nombre, apellido, edad) {
  return {
    nombre,
    apellido,
    edad,
    saludo() {
      return `${this.nombre} ${this.apellido}`
    },
    esMayorDeEdad() {
      return this.edad >= 18
    }
  }
}

const persona1 = crearPersona("Juan", "Perez", 20)
const persona2 = crearPersona("Maria", "Gomez", 15)

Esta forma de crear objetos es muy común, pero también nos plantea varios problemas de rendimiento en caso de que necesitemos crear muchos objetos, además, no es muy escalable, ya que si necesitamos agregar un nuevo método, tenemos que hacerlo en cada objeto.

Para solucionar estos problemas, podemos utilizar las clases.

Class

Las clases nos permiten crear objetos de forma más eficiente, y nos permiten agregar nuevos métodos y atributos de forma más sencilla. Es importante tener en cuenta que las clases no son un nuevo tipo de dato, sino que son una forma de crear objetos.

Para crear una clase, utilizamos la palabra reservada class, seguida del nombre de la clase, y entre llaves, los atributos y métodos que va a tener la clase.

javascript
class Persona {
  constructor(nombre, apellido, edad) {
    this.nombre = nombre
    this.apellido = apellido
    this.edad = edad
  }

  saludo() {
    return `${this.nombre} ${this.apellido}`
  }

  esMayorDeEdad() {
    return this.edad >= 18
  }
}
class Persona {
  constructor(nombre, apellido, edad) {
    this.nombre = nombre
    this.apellido = apellido
    this.edad = edad
  }

  saludo() {
    return `${this.nombre} ${this.apellido}`
  }

  esMayorDeEdad() {
    return this.edad >= 18
  }
}

La clase Persona tiene tres atributos: nombre, apellido y edad, y dos métodos: saludo y esMayorDeEdad.

El método constructor es un método especial que se ejecuta al instanciar una clase, y nos permite inicializar los atributos de la clase, que estarán disponibles en todos los métodos de la clase, mediante la palabra reservada this.

Crear objetos a partir de clases

Para crear un objeto a partir de una clase, utilizamos la palabra reservada new, seguida del nombre de la clase, y entre paréntesis, los argumentos que recibe el método constructor.

javascript
const persona1 = new Persona("Juan", "Perez", 20)
const persona2 = new Persona("Maria", "Gomez", 15)
const persona1 = new Persona("Juan", "Perez", 20)
const persona2 = new Persona("Maria", "Gomez", 15)

Ahora podemos usar cualquier metodo de la clase en los objetos creados a partir de ella.

javascript
console.log(persona1.saludo())
console.log(persona2.saludo())
console.log(persona1.esMayorDeEdad())
console.log(persona2.esMayorDeEdad())
console.log(persona1.saludo())
console.log(persona2.saludo())
console.log(persona1.esMayorDeEdad())
console.log(persona2.esMayorDeEdad())

Herencia

La herencia es un mecanismo que nos permite crear nuevas clases a partir de otras clases, y heredar los atributos y métodos de la clase padre.

Vamos a ver la siguiente clase:

javascript
class Recipiente {
  constructor(material, capacidad) {
    this.material = material
    this.capacidad = capacidad
    this.contenido = 0
  }

  llenar() {
    this.contenido = this.capacidad
  }

  vaciar() {
    this.contenido = 0
  }

  beber() {
    this.contenido -= 1
  }

  mostrarContenido() {
    console.log(this.contenido)
  }
}

class Botella extends Recipiente {
  constructor(material, capacidad, tapa) {
    super(material, capacidad)
    this.tapa = tapa
  }
  beber() {
    if (this.tapa) {
      console.log("No se puede beber, la botella está tapada")
    } else {
      super.beber()
    }
  }
  destapar() {
    this.tapa = false
  }

  tapar() {
    this.tapa = true
  }
}
class Recipiente {
  constructor(material, capacidad) {
    this.material = material
    this.capacidad = capacidad
    this.contenido = 0
  }

  llenar() {
    this.contenido = this.capacidad
  }

  vaciar() {
    this.contenido = 0
  }

  beber() {
    this.contenido -= 1
  }

  mostrarContenido() {
    console.log(this.contenido)
  }
}

class Botella extends Recipiente {
  constructor(material, capacidad, tapa) {
    super(material, capacidad)
    this.tapa = tapa
  }
  beber() {
    if (this.tapa) {
      console.log("No se puede beber, la botella está tapada")
    } else {
      super.beber()
    }
  }
  destapar() {
    this.tapa = false
  }

  tapar() {
    this.tapa = true
  }
}

La clase Recipiente tiene cuatro atributos: material, capacidad, contenido y tapa, y cuatro métodos: llenar, vaciar, beber y mostrarContenido.

La clase Botella hereda de la clase Recipiente, y tiene un atributo adicional: tapa, y dos métodos adicionales: destapar y tapar. Además, el método beber de la clase Botella se sobreescribe, y se agrega una validación para verificar si la botella está tapada o no.

Para heredar de una clase, utilizamos la palabra reservada extends, seguida del nombre de la clase de la que queremos heredar.

El método constructor de la clase Botella utiliza la palabra reservada super, que nos permite llamar al método constructor de la clase padre (en este caso Recipiente), y pasarle los argumentos que recibe.

Argumentos por defecto

Los argumentos por defecto nos permiten asignar un valor por defecto a los argumentos de una función, en caso de que no se le pase un valor al llamarla.

javascript
function repeat(str = "texto de ejemplo", times = 2) {
  let result = ""
  for (let i = 0; i < times; i++) {
    result += str
  }
  return result
}
console.log(repeat())
console.log(repeat("Hola"))
console.log(repeat("Hola", 3))
console.log(repeat("Hola", 8))
function repeat(str = "texto de ejemplo", times = 2) {
  let result = ""
  for (let i = 0; i < times; i++) {
    result += str
  }
  return result
}
console.log(repeat())
console.log(repeat("Hola"))
console.log(repeat("Hola", 3))
console.log(repeat("Hola", 8))

En las clases también podemos utilizar argumentos por defecto, tanto en el método constructor como en los demás métodos.

javascript
class Persona {
  constructor(nombre = "Juan", apellido = "Perez", edad = 20) {
    this.nombre = nombre
    this.apellido = apellido
    this.edad = edad
  }

  saludo() {
    return `${this.nombre} ${this.apellido}`
  }

  esMayorDeEdad() {
    return this.edad >= 18
  }
}

const persona1 = new Persona()
const persona2 = new Persona("Maria", "Gomez", 15)
class Persona {
  constructor(nombre = "Juan", apellido = "Perez", edad = 20) {
    this.nombre = nombre
    this.apellido = apellido
    this.edad = edad
  }

  saludo() {
    return `${this.nombre} ${this.apellido}`
  }

  esMayorDeEdad() {
    return this.edad >= 18
  }
}

const persona1 = new Persona()
const persona2 = new Persona("Maria", "Gomez", 15)

Getters y setters

Los getters y setters son métodos especiales que nos permiten acceder y modificar los atributos de una clase, respectivamente.

javascript

class Persona {
  constructor(nombre, apellido, edad) {
    this.nombre = nombre
    this.apellido = apellido
    this.edad = edad
  }

  get nombreCompleto() {
    return `${this.nombre} ${this.apellido}`
  }

  set nombreCompleto(nombreCompleto) {
    const [nombre, apellido] = nombreCompleto.split(" ")
    this.nombre = nombre
    this.apellido = apellido
  }

  get esMayorDeEdad() {
    return this.edad >= 18
  }

  set esMayorDeEdad(esMayorDeEdad) {
    if (esMayorDeEdad) {
      this.edad = 18
    } else {
      this.edad = 17
    }
  }
}

const persona1 = new Persona("Juan", "Perez", 20)
console.log(persona1.nombreCompleto)
persona1.nombreCompleto = "Maria Gomez"
console.log(persona1.nombreCompleto)
console.log(persona1.esMayorDeEdad)
persona1.esMayorDeEdad = false
console.log(persona1.esMayorDeEdad)

class Persona {
  constructor(nombre, apellido, edad) {
    this.nombre = nombre
    this.apellido = apellido
    this.edad = edad
  }

  get nombreCompleto() {
    return `${this.nombre} ${this.apellido}`
  }

  set nombreCompleto(nombreCompleto) {
    const [nombre, apellido] = nombreCompleto.split(" ")
    this.nombre = nombre
    this.apellido = apellido
  }

  get esMayorDeEdad() {
    return this.edad >= 18
  }

  set esMayorDeEdad(esMayorDeEdad) {
    if (esMayorDeEdad) {
      this.edad = 18
    } else {
      this.edad = 17
    }
  }
}

const persona1 = new Persona("Juan", "Perez", 20)
console.log(persona1.nombreCompleto)
persona1.nombreCompleto = "Maria Gomez"
console.log(persona1.nombreCompleto)
console.log(persona1.esMayorDeEdad)
persona1.esMayorDeEdad = false
console.log(persona1.esMayorDeEdad)

Los getters y setters se definen como métodos, pero se acceden como atributos, sin utilizar paréntesis. Los getters se utilizan para obtener el valor de un atributo, y los setters se utilizan para modificar el valor de un atributo, esto nos da un mayor control sobre los atributos de una clase con una sintaxis más sencilla.

Métodos estáticos

Los métodos estáticos son métodos que se pueden utilizar sin necesidad de instanciar una clase, y se definen utilizando la palabra reservada static.

javascript
class Persona {
  constructor(nombre, apellido, edad) {
    this.nombre = nombre
    this.apellido = apellido
    this.edad = edad
  }

  static crear(nombre, apellido, edad) {
    return new Persona(nombre, apellido, edad)
  }

  saludo() {
    return `${this.nombre} ${this.apellido}`
  }

  esMayorDeEdad() {
    return this.edad >= 18
  }
}

const persona1 = Persona.crear("Juan", "Perez", 20)
const persona2 = Persona.crear("Maria", "Gomez", 15)
class Persona {
  constructor(nombre, apellido, edad) {
    this.nombre = nombre
    this.apellido = apellido
    this.edad = edad
  }

  static crear(nombre, apellido, edad) {
    return new Persona(nombre, apellido, edad)
  }

  saludo() {
    return `${this.nombre} ${this.apellido}`
  }

  esMayorDeEdad() {
    return this.edad >= 18
  }
}

const persona1 = Persona.crear("Juan", "Perez", 20)
const persona2 = Persona.crear("Maria", "Gomez", 15)

Los métodos estáticos se definen como métodos, pero se acceden como atributos, sin utilizar paréntesis. Los métodos estáticos se utilizan para crear funciones que no dependan de una instancia de la clase, y que puedan ser utilizadas sin necesidad de instanciar la clase.

Esto es útil para crear funciones que no dependan de una instancia de la clase, y que puedan ser utilizadas sin necesidad de instanciar la clase.