Inicio Clases en JavaScript (ES6)
Artículo
Cancelar

Clases en JavaScript (ES6)

Introducción

Como ya se ha explicado anteriormente, en JavaScript, todas las clases son en realidad funciones constructoras, que son a su vez objetos. Aunque JavaScript es un lenguaje basado en prototipos (classless), ES6 introdujo la palabra reservada class para proporcionar una sintaxis más familiar y cómoda para los programadores provenientes de otros lenguajes orientados a objetos. Sin embargo, bajo esta sintaxis, JavaScript sigue funcionando con prototipos.

Las clases en JavaScript pueden definirse de dos maneras: utilizando funciones constructoras o la sintaxis de clase de ES6.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Funciones Constructoras

function Hero(name, level) {
    this.name = name;
    this.level = level;
}
const hero1 = new Hero('Link', 10);
console.log(hero1); // Hero { name: 'Link', level: 10 }


// Sintaxis de Clase


class HeroClass {
    constructor(name, level) {
        this.name = name;
        this.level = level;
    }
}
const hero2 = new HeroClass('Zelda', 20);
console.log(hero2); // Hero { name: 'Zelda', level: 20 }
1
2
Hero { name: "Link", level: 10 }
HeroClass { name: "Zelda", level: 20 }

En ambos ejemplos, se crea una clase Hero con un constructor que inicializa las propiedades name y level.

Creación de Métodos

Los métodos en una clase pueden definirse directamente dentro del constructor utilizando funciones constructoras o como métodos de clase en la sintaxis ES6.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// Funciones Constructoras
(()=>{
function Hero(name, level) {
    this.name = name;
    this.level = level;
}

Hero.prototype.greet = function() {
    return `${this.name} says hello.`;
};
const hero3 = new Hero('Mario', 5);
console.log(hero3.greet()); // Mario says hello.
})();
// Sintaxis de Clase
(()=>{
class Hero {
    constructor(name, level) {
        this.name = name;
        this.level = level;
    }

    greet() {
        return `${this.name} says hello.`;
    }
}
const hero4 = new Hero('Luigi', 7);
console.log(hero4.greet()); // Luigi says hello.
})()
1
2
Mario says hello.
Luigi says hello.

En ambos casos, se añade un método greet que devuelve un saludo del héroe.

Herencia

La herencia permite crear una nueva clase que hereda las propiedades y métodos de otra clase.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// Funciones Constructoras

function Mage(name, level, spell) {
    Hero.call(this, name, level);
    this.spell = spell;
}

Mage.prototype = Object.create(Hero.prototype);
Mage.prototype.constructor = Mage;

const mage1 = new Mage('Gandalf', 100, 'Fireball');
console.log(mage1); // Mage { name: 'Gandalf', level: 100, spell: 'Fireball' }


// Sintaxis de Clase
(()=>{
class Mage extends Hero {
    constructor(name, level, spell) {
        super(name, level);
        this.spell = spell;
    }
}

const mage2 = new Mage('Merlin', 150, 'Ice Blast');
console.log(mage2); // Mage { name: 'Merlin', level: 150, spell: 'Ice Blast' }
})()
1
2
Mage { name: "Gandalf", level: 100, spell: "Fireball" }
Mage { name: "Merlin", level: 150, spell: "Ice Blast" }

En estos ejemplos, Mage extiende Hero, añadiendo una nueva propiedad spell.

Atributos Estáticos

Los atributos y métodos estáticos se definen en la clase en lugar de en las instancias.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Foo {
    constructor(prop) {
        this.prop = prop;
    }

    static staticMethod() {
        return 'classy';
    }

    prototypeMethod() {
        return 'prototypical';
    }
}

const foo = new Foo(123);
console.log(Foo.staticMethod()); // classy
console.log(foo.prototypeMethod()); // prototypical
1
2
classy
prototypical

En este ejemplo, staticMethod se llama en la clase Foo, mientras que prototypeMethod se llama en la instancia foo.

alt text

Atributos Privados

Por defecto, en ES6, todo es público. Sin embargo, ES2019 introdujo la sintaxis # para declarar atributos privados. Alternativamente, se pueden utilizar funciones internas y scopes para emular privacidad.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// Con Sintaxis ES2019
(()=>{

class SmallRectangle {
    #width = 20;
    #height = 10;

    getDimension() {
        return { width: this.#width, height: this.#height };
    }

    increaseSize() {
        this.#width++;
        this.#height++;
    }
}

const rectangle = new SmallRectangle();
console.log(rectangle.getDimension()); // { width: 20, height: 10 }
console.log(rectangle.height); // undefined
console.log(rectangle.width); // undefined
})();

// Utilizando Closure

(()=>{
class SmallRectangle {
    constructor() {
        let width = 20;
        let height = 10;

        this.getDimension = () => {
            return { width, height };
        };

        this.increaseSize = () => {
            width++;
            height++;
        };
    }
}

const rectangle = new SmallRectangle();
console.log(rectangle.getDimension()); // { width: 20, height: 10 }
console.log(rectangle.height); // undefined
console.log(rectangle.width); // undefined
})()
1
2
3
4
5
6
{ width: 20, height: 10 }
undefined
undefined
{ width: 20, height: 10 }
undefined
undefined

En ambos ejemplos, width y height son privados y solo accesibles a través de métodos específicos.

Closure

Una técnica importante para manejar variables privadas y comportamiento similar a las clases es el uso de closures.

1
2
3
4
5
6
7
8
9
10
let add = (function () {
    let counter = 0;
    return function () {
        counter += 1;
        return counter;
    };
})();

console.log(add()); // 1
console.log(add()); // 2
1
2
1
2

En este ejemplo, add es una función autoinvocada que mantiene un counter privado.

Habrás observado que en muchos ejemplos, envuelvo el código en (()=>{ ....codigo.... })(). Esto es para mantener las variables creadas en el ámbito de esa función autoinvocada y que no moleste al resto del notebook. Esto es un ejemplo de closure.

(Voluntario) Lee el artículo https://github.com/getify/You-Dont-Know-JS/blob/2nd-ed/get-started/ch3.md#closure.

Setters y Getters

Los setters y getters permiten controlar cómo se acceden y modifican las propiedades de una clase.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Producto {
    constructor(nombre, precio) {
        this.nombre = nombre;
        this.precio = precio;
    }

    set setPrecio(precio) {
        if (isNaN(precio)) {
            this.precio = 0;
        } else {
            this.precio = precio;
        }
    }

    get getPrecio() {
        return parseFloat(this.precio);
    }
}

let p1 = new Producto('PC', 1000);
p1.setPrecio = 900;
console.log(p1.getPrecio); // 900
1
900

En este ejemplo, el setter setPrecio valida el valor antes de asignarlo, y el getter getPrecio devuelve el precio como un número flotante.

Bibliografía

Este artículo está licenciado bajo CC BY 4.0 por el autor.

Prototipos en JavaScript

-