13. Las clases

Aquí presentamos las clases de ECMAScript 6. En primer lugar, mostramos que las funciones ya pueden utilizarse como clases.
13.1. script [class-00]
El siguiente script muestra un uso poco habitual de las funciones. Aquí se utilizan como objetos.
'use strict';
// una función se puede utilizar como un objeto
// una carcasa vacía
function f() {
}
// a la que se le asignan propiedades desde el exterior
f.prop1 = "val1";
f.show = function () {
console.log(this.prop1);
}
// uso de f
f.show();
// una función g que funciona como una clase
function g() {
this.prop2 = "val2";
this.show = function () {
console.log(this.prop2);
}
}
// instanciación de la función con [new]
new g().show();
Comentarios
- líneas 5-7: el cuerpo de la función f no define ninguna propiedad;
- líneas 9-12: se asignan propiedades a la función f desde el exterior;
- línea 14: uso de la función (objeto) f. Fíjate en que no se escribe [f()], sino simplemente [f]. Aquí se utiliza la notación de un objeto;
- líneas 17-22: se define una función [g] como si fuera una clase con propiedades y métodos;
- línea 24: la función [g] es instanciada por [new g()];
Resultados de la ejecución
[Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Data\st-2019\dev\es6\javascript\classes\class-00.js"
val1
val2
ES6 ha introducido el concepto de clase, lo que nos permite ahora evitar tener que recurrir a funciones para obtener clases.
13.2. script [class-01]
El script [class-01] presenta una clase [Personne]:
// clase
class Personne {
// constructor
constructor(nom, prénom, âge) {
this.nom = nom;
this.prénom = prénom;
this.âge = âge;
}
// métodos getter y setter
get nom() {
return this._nom;
}
set nom(value) {
this._nom = value;
}
get prénom() {
return this._prénom;
}
set prénom(value) {
this._prénom = value;
}
get âge() {
return this._âge;
}
set âge(value) {
this._âge = value;
}
// toString en JSON
toString() {
return JSON.stringify(this);
}
}
// llamada a la clase
function main() {
const personne = new Personne("Poirot", "Hercule", 66);
console.log("personne=", personne.toString(), typeof (personne), personne instanceof (Personne));
}
// llamada a main
main();
Comentarios
- línea 2: la palabra clave [class] designa una clase;
- líneas 5-9: la palabra clave [constructor] designa el constructor de la clase. Solo puede haber uno como máximo. Sirve para crear e inicializar una instancia de la clase. Ten en cuenta que no hay ninguna declaración de las propiedades [nom, prénom, âge];
- líneas 11-36: propiedades de la clase. Aquí encontramos elementos que ya hemos visto en el apartado sobre objetos 4, página 44. Solo difiere la sintaxis;
- línea 41: creación de un objeto de tipo [Personne]. A partir de ahora, el objeto [personne] se utiliza como un objeto literal. [typeof (personne)] es igual a «object» y la expresión [personne instanceof (Personne)] es verdadera. Por lo tanto, es posible conocer el tipo exacto de una instancia de clase;
Resultados de la ejecución
[Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Data\st-2019\dev\es6\javascript\classes\class-01.js"
personne= {"_nom":"Poirot","_prénom":"Hercule","_âge":66} object true
13.3. script [class-02]
Este script muestra la posibilidad de heredar de una clase con la palabra clave [extends].
En primer lugar, aislamos la clase [Personne] en un módulo [Personne.js]:
// clase
class Personne {
// constructor
constructor(nom, prénom, âge) {
this.nom = nom;
this.prénom = prénom;
this.âge = âge;
}
// getters y setters
get nom() {
return this._nom;
}
set nom(value) {
this._nom = value;
}
get prénom() {
return this._prénom;
}
set prénom(value) {
this._prénom = value;
}
get âge() {
return this._âge;
}
set âge(value) {
this._âge = value;
}
// toString en JSON
toString() {
return JSON.stringify(this);
}
}
// clase de exportación
export default Personne;
- línea 39: exportamos la clase [Personne] para que otros scripts puedan importarla;
El script [class-02] crea una clase [Enseignant] derivada de la clase [Personne]:
// importaciones
import Personne from './Personne';
// clase
class Enseignant extends Personne {
// constructor
constructor(nom, prénom, âge, discipline) {
super(nom, prénom, âge);
this.discipline = discipline;
}
// getters y setters
get discipline() {
return this._discipline;
}
set discipline(value) {
this._discipline = value;
}
}
// llamada a la clase
function main() {
const enseignant = new Enseignant("Poirot", "Hercule", 66, "détective");
console.log("enseignant=", enseignant.toString(), typeof (enseignant), enseignant instanceof Enseignant);
}
// llamada a main
main();
Comentarios
- línea 2: se importa la clase [Personne] desde el módulo [Personne.js], que se encuentra en la misma carpeta que [class-02];
- línea 5: la clase [Enseignant] extiende (hereda de) la clase [Personne] con la palabra clave [extends]: le añade una propiedad [_discipline] con los getter y setter correspondientes;
- líneas 8-11: el constructor de la clase [Enseignant] recibe cuatro valores para inicializar las cuatro propiedades de la clase;
- línea 9: la palabra clave [super] invoca al constructor de la clase padre [Personne], que, por lo tanto, inicializará las propiedades [_nom, _prénom, _âge];
- línea 10: se inicializa la propiedad [_discipline], que pertenece a la clase [Enseignant];
- líneas 14-19: el getter y el setter de la propiedad [_discipline];
- línea 25: se crea un objeto de tipo [Enseignant];
- línea 26: se utiliza el método [enseignant.toString()]. La clase [Enseignant] no dispone de este método. En ese caso, se utiliza automáticamente el de su clase padre. Este método hace que la expresión [JSON.stringify(this)] —donde [this] será aquí un objeto [Enseignant] y no un objeto [Personne]— se resuelva correctamente. Esto es lo que en programación orientada a objetos se denomina polimorfismo de clases. Un término complicado para JavaScript, que no es un lenguaje orientado a objetos. No obstante, JavaScript hace aquí lo que se espera de él: muestra correctamente a un profesor;
Los resultados de la ejecución son los siguientes:
[Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Data\st-2019\dev\es6\javascript\classes\class-02.js"
enseignant= {"_nom":"Poirot","_prénom":"Hercule","_âge":66,"_discipline":"détective"} object true
- línea 2: JavaScript reconoce correctamente que la variable [enseignant] es de tipo [Enseignant];
13.4. script [class-03]
El script [class-03] muestra que una clase hija puede redefinir las propiedades y métodos de su clase padre. Aquí redefinimos el método [toString] de la clase padre:
// importaciones
import Personne from './Personne';
// clase
class Enseignant extends Personne {
// constructor
constructor(nom, prénom, âge, discipline) {
super(nom, prénom, âge);
this.discipline = discipline;
}
// getters y setters
get discipline() {
return this._discipline;
}
set discipline(value) {
this._discipline = value;
}
// redefinición de toString
toString() {
return "[Enseignant]" + JSON.stringify(this);
}
}
// llamada a la clase
function main() {
const enseignant = new Enseignant("Poirot", "Hercule", 66, "détective");
console.log("enseignant=", enseignant.toString(), typeof (enseignant), enseignant instanceof Enseignant);
}
// llamada a main
main();
Los resultados de la ejecución son los siguientes:
[Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Data\st-2019\dev\es6\javascript\classes\class-03.js"
enseignant= [Enseignant]{"_nom":"Poirot","_prénom":"Hercule","_âge":66,"_discipline":"détective"} object true
13.5. script [class-04]
El script [class-04] vuelve a mostrar el polimorfismo en acción: cuando una función espera un parámetro formal de tipo [Personne], se puede pasar un tipo derivado como [Enseignant]. De hecho, debido a la derivación, el tipo [Enseignant] tiene todos los atributos del tipo [Personne].
En primer lugar, aislamos el tipo [Enseignant] en un módulo [Enseignant.js]:
// importaciones
import Personne from './Personne';
// clase
class Enseignant extends Personne {
// constructor
constructor(nom, prénom, âge, discipline) {
super(nom, prénom, âge);
this.discipline = discipline;
}
// getter y setter
get discipline() {
return this._discipline;
}
set discipline(value) {
this._discipline = value;
}
}
// clase de exportación
export default Enseignant;
- línea 24: se exporta la clase [Enseignant] para que otros scripts puedan importarla;
El script [class-04] es el siguiente:
// importaciones
import Enseignant from './Enseignant';
import Personne from './Personne';
// función que acepta una persona como parámetro
function show(personne) {
// en todos los casos
console.log("paramètre=", personne.toString(), typeof (personne));
// instancia de Persona
if (personne instanceof Personne) {
console.log("personne=", personne.toString());
}
// instancia de Profesor
if (personne instanceof Enseignant) {
console.log("enseignant=", personne.toString());
}
}
// llamada a «show» con un profesor
show(new Enseignant("Poirot", "Hercule", 66, "détective"));
show(new Personne("Marple", "Miss", 70));
- línea 6: la función [show] espera un tipo [Personne] o derivado;
- línea 8: se muestra la cadena del parámetro y su tipo. Aparecerá [object];
- líneas 10-16: se puede determinar si se trata de un tipo [Personne] o de un tipo [Enseignant]. Por lo tanto, el código se puede adaptar al tipo real del parámetro;
Los resultados de la ejecución son los siguientes:
[Running] C:\myprograms\laragon-lite\bin\nodejs\node-v10\node.exe -r esm "c:\Data\st-2019\dev\es6\javascript\classes\class-04.js"
paramètre= {"_nom":"Poirot","_prénom":"Hercule","_âge":66,"_discipline":"détective"} object
personne= {"_nom":"Poirot","_prénom":"Hercule","_âge":66,"_discipline":"détective"}
enseignant= {"_nom":"Poirot","_prénom":"Hercule","_âge":66,"_discipline":"détective"}
paramètre= {"_nom":"Marple","_prénom":"Miss","_âge":70} object
personne= {"_nom":"Marple","_prénom":"Miss","_âge":70}
- líneas 4 y 6: JavaScript reconoce correctamente el tipo de las instancias de clase;