Clases

<< JSON Herencia >>

Desde la versión ES2015 se ha estandarizado una sintaxis más moderna para crear clases en JavaScript, similar a otros lenguajes orientados a objetos, evitando el concepto de funciones constructoras y uso de prototype explícitamente (aunque internamente siga funcionando así)

class Product { }

console.log(typeof Product); // Imprime "function". Internamente sigue siendo una función como en versiones antiguas

Constructor

Para crear un constructor y asignar atributos a los objetos instanciados a partir de la clase, tenemos que implementar un método llamado constructor().

Dentro del constructor y de otros métodos de la clase, debemos usar la palabra reservada this para acceder a las propiedades del objeto actual, ya sea para asignar nuevos valores, o leerlos. Al contrario que en otros lenguajes fuertemente tipados, no necesitamos declarar los atributos del objeto fuera del constructor (aunque se puede hacer), basta con asignarlos.

class Product {
    constructor(nombre, precio) {
        this.nombre = nombre;
        this.precio = precio;
    }
}

let p = new Product("Producto", 50);
console.log(p); // Product {nombre: "Producto", precio: 50}

Métodos

Así es como declaramos métodos en una clase. Estos métodos tienen acceso al objeto desde el cual se invocan utilizando la referencia this.

class Product {
    ///...
    getDescuento(descuento) {
        let totalDesc = this.precio * descuento / 100;
        return this.precio - totalDesc;
    }
  }

let p = new Product("Producto", 50);
console.log(p.getDescuento(20)); // Imprime 40

Ámbito público/privado

Desde la versión ES2022 se pueden declarar atributos y métodos privados en una clase. Para declararlos como privados, se debe poner una almohadilla '#' delante del nombre del atributo o método en cuestión. De esta manera no podrá ser accedido desde fuera de la clase.

Esto implica que si queremos acceder a atributos privados de un objeto, lo tenemos que hacer mediante métodos (getters/setters), o propiedades públicas. Vamos a ver ambas opciones a continuación. Esto permite controlar el acceso, y por ejemplo, controlar el valor que se le asigna a un atributo, o directamente no permitir el cambio de valor (solo lectura).

class User {
    #name;

    constructor(name) {
        this.#name = name;
    }
}

let u = User("Pepe");
console.log(u.#name); // ERROR: Uncaught SyntaxError: Private field '#name' must be declared in an enclosing class

Getters/setters estilo Java

Se basa en declarar métodos con el mismo nombre que los atributos y con el prefijo get o set. Llamamos al método en cuestión para acceder o modificar el atributo.

class User {
    #name;

    constructor(name) {
        this.setName(name);
    }

    getName() { // Getter
        return this.#name;
    }

    setName(name) { // Setter
        this.#name = name;
    }
}

let user = new User("john");
user.setName("Alex");
console.log(user.getName()); // Alex

Getters/setters estilo C#/Python

También conocido como propiedades públicas. Internamente funciona igual, pero en este caso el prefijo (get/set) se separa por un espacio. Desde fuera se utiliza como si accedieramos a un atributo público, pero cuando leemos el valor o lo sobrescribimos, por debajo se llama al getter/setter correspondiente. Se podría decir que es una forma más natural de acceder a los atributos de un objeto.

class User {
    #name;

    constructor(name) {
        this.name = name; // Llamada implícita al setter
    }

    get name() { // Getter
        return this.#name;
    }

    set name(name) { // Setter
        this.#name = name;
    }
  }

let user = new User("john");
user.name = "Alex"; // Llamada implícita al setter
console.log(user.name); // Llamada implícita al getter

Propiedades estáticas

En JavaScript moderno se pueden declarar tanto propiedades como métodos de clase, es decir, independientes de cualquier objeto instanciado. Para ello se pone delante la palabra reservada static. Debemos acceder a dichas propiedades y métodos utilizando el nombre de la clase como prefijo seguida de un punto.

class Empleado {
    static #sueldoMinimo = 15000;

    constructor(nombre, sueldo) {
        this.nombre = nombre;
        this.sueldo = sueldo;
    }

    static creaBecario(nombre) {
        return new Empleado(nombre, Empleado.#sueldoMinimo);
    }
}

let e = Empleado.creaBecario("Elena");
console.log(e); // Empleado {nombre: 'Elena', sueldo: '15000'}

<< JSON Herencia >>