Comprensión profunda de los principios fundamentales de las clases (class) en ES6

ES6 introdujo la característica de las clases (class), haciendo que el estilo de programación orientada a objetos en JavaScript sea más sencillo y fácil de entender. Aunque las clases parecen un modelo orientado a objetos clásico como en otros lenguajes de programación, su base sigue siendo el mecanismo de herencia basado en prototipos que ya existía en JavaScript. Este artículo profundiza en los principios fundamentales de las clases en ES6 para ayudarte a comprender mejor su funcionamiento.

1. La clase es esencialmente un constructor

Una clase definida en ES6 es en realidad un constructor. Puedes verificar esto usando la palabra clave typeof:

1
class Person {
2
constructor(name) {
3
this.name = name;
4
}
5
}
6
7
console.log(typeof Person); // "function"

La definición de una clase se compila finalmente como una función, lo que significa que una clase es solo una forma simplificada de escribir un constructor.

2. Funcionamiento del constructor

El método constructor de una clase se utiliza para inicializar instancias de la clase. Al crear una instancia, la palabra clave new llama al constructor:

1
class Person {
2
constructor(name) {
3
this.name = name;
4
}
5
}
6
7
const p = new Person("Alice");
8
console.log(p.name); // "Alice"

Esto es muy similar a cómo funcionan los constructores de funciones en ES5:

1
function Person(name) {
2
this.name = name;
3
}
4
5
const p = new Person("Alice");
6
console.log(p.name); // "Alice"

3. Herencia basada en prototipos

Los métodos definidos en una clase se agregan a su prototipo, lo que es consistente con el mecanismo de herencia basado en prototipos en ES5. Cada vez que defines un método en una clase, este se añade al prototipo de la clase:

1
class Person {
2
constructor(name) {
3
this.name = name;
4
}
5
6
greet() {
7
console.log(`Hello, ${this.name}!`);
8
}
9
}
10
11
const p = new Person("Alice");
12
p.greet(); // "Hello, Alice!"

En ES5, el mismo resultado se puede lograr de la siguiente manera:

1
function Person(name) {
2
this.name = name;
3
}
4
5
Person.prototype.greet = function () {
6
console.log(`Hello, ${this.name}!`);
7
};
8
9
const p = new Person("Alice");
10
p.greet(); // "Hello, Alice!"

El mecanismo de herencia de las clases sigue siendo a través de la cadena de prototipos.

4. Mecanismo de herencia de clases

ES6 proporciona la palabra clave extends para permitir la herencia entre clases, que depende del mecanismo de herencia de prototipos en JavaScript. Cuando una subclase hereda de una clase base, el prototipo de la subclase apunta al prototipo de la clase base, lo que permite la herencia de métodos:

1
class Animal {
2
constructor(name) {
3
this.name = name;
4
}
5
6
speak() {
7
console.log(`${this.name} hace un ruido.`);
8
}
9
}
10
11
class Dog extends Animal {
12
speak() {
13
console.log(`${this.name} ladra.`);
14
}
15
}
16
17
const d = new Dog("Rex");
18
d.speak(); // "Rex ladra."

Esto es equivalente a cómo se implementaría manualmente la herencia en ES5:

1
function Animal(name) {
2
this.name = name;
3
}
4
5
Animal.prototype.speak = function () {
6
console.log(`${this.name} hace un ruido.`);
7
};
8
9
function Dog(name) {
10
Animal.call(this, name);
11
}
12
13
Dog.prototype = Object.create(Animal.prototype);
14
Dog.prototype.constructor = Dog;
15
16
Dog.prototype.speak = function () {
17
console.log(`${this.name} ladra.`);
18
};
19
20
const d = new Dog("Rex");
21
d.speak(); // "Rex ladra."

5. Uso de super para llamar a los métodos de la clase padre

La palabra clave super permite que una subclase llame al constructor y a los métodos de la clase padre. En el constructor de una subclase, super se usa para llamar al constructor de la clase base, heredando así sus propiedades y métodos:

1
class Animal {
2
constructor(name) {
3
this.name = name;
4
}
5
}
6
7
class Dog extends Animal {
8
constructor(name, breed) {
9
super(name); // Llamada al constructor de la clase padre
10
this.breed = breed;
11
}
12
}
13
14
const d = new Dog("Rex", "Labrador");
15
console.log(d.name); // "Rex"
16
console.log(d.breed); // "Labrador"

En ES5, se puede lograr un efecto similar llamando explícitamente al constructor de la clase padre:

1
function Animal(name) {
2
this.name = name;
3
}
4
5
function Dog(name, breed) {
6
Animal.call(this, name); // Llamada al constructor de la clase padre
7
this.breed = breed;
8
}
9
10
const d = new Dog("Rex", "Labrador");
11
console.log(d.name); // "Rex"
12
console.log(d.breed); // "Labrador"

Conclusión

La introducción de las clases en ES6 proporciona una sintaxis más concisa para la programación orientada a objetos en JavaScript, pero su base sigue dependiendo de los constructores y la cadena de prototipos. Las clases son esencialmente una forma simplificada de escribir constructores, y su mecanismo de herencia se basa en la cadena de prototipos. Con super, las subclases pueden llamar al constructor y a los métodos de la clase base, permitiendo la relación de herencia. Comprender los principios fundamentales de las clases ayuda a aprovechar mejor las características orientadas a objetos de JavaScript en el desarrollo diario.