JavaScript 2015 (VIII). POO en JS6.

Facebooktwittergoogle_pluslinkedinmailFacebooktwittergoogle_pluslinkedinmail

La programación orientada a objetos siempre ha sido una asignatura pendiente en JavaScript. Si bien es cierto que este leguaje incorpora, desde hace mucho tiempo, clases propias como Array, Date, JSON o Math, por ejemplo, con sus propiedades o métodos, carecíamos de la posibilidad de crear nuestras propias clases y utilizar una sintaxis específica de POO como tenemos en otros lenguajes. Sí. Es cierto que podemos crear arreglos de variables y gestionarlas, hasta cierto punto, como si fueran propiedades de un objeto

JS6 soluciona esa carencia, permitiéndonos crear clases, emplear herencia, y disponer de las funcionalidades de la POO. Es cierto que el mecanismo proporcionado aún podría mejorarse y, seguramente, evolucionará en un futuro próximo. No obstante, tal cómo está ya constituye un paso de gigante que no podemos dejar de analizar en esta serie de artículos.

Este artículo está orientado a la POO en JavaScript 6. No es un manual de POO y, aunque repasamos algunos conceptos generales, entiendo que estás familiarizado con las generalidades de la POO, aunque la hayas usado en otros lenguajes.

CREAR UNA CLASE

Vamos a ver como creamos una clase en JS6, con su estructura básica. Empecemos con este código:

El resultado que vemos en la página es el siguiente:

Nombre: Pedro
Apellidos: García Muñoz

Analicemos lo que tenemos. Las clases, al igual que en otros lenguajes, se decalran con la palabra reservada class, seguida del nombre de la clase que vamos a crear. La palabra class no existía hasta ahora en JavaScript, por lo que nos estaba vedado crear estas estructuras. Lo vemos en la línea 10. Como puedes ver, todo el código de la clase se acota con llaves.

En el interior de la clase se crean los métodos que se vayan a emplear. La forma de crear un método es similar a la declaración de funciones de usuario, pero sin emplear la palabra function. Lo normal es empezar la definición de la clase creando el método constructor, que se denomina, precisamente, así: constructor(). El método constructor no necesita ser invocado desde ninguna parte del código. Cuando se instancia un objeto de la clase, el método constructor se ejecuta automáticamente. El método constructor no es obligatorio, pero sí muy recomendable por razones organizativas y de claridad del código. En este sentido, se ha seguido la misma forma de trabajo que en otros lenguajes.

Observa en el ejemplo que el constructor recibe dos argumentos, que asigna a dos variables de instancia (de la instancia que se está creando en cada momento). Para ello emplea el comodín this que, como sin duda ya sabes, se refiere siempre al objeto en curso. Ves la forma en que actúa entre las líneas 11 y 14 del código.

La forma de crear un nuevo objeto de una clase es la misma que en cualquier lenguaje que use el paradigma POO: mediante el operador new, seguido del nombre de la clase y un juego de paréntesis. Entre estos paréntesis se incluyen los argumentos que le queramos pasar al constructor, ya que, como hemos comentado, este se ejecuta automáticamente al instanciar la clase en un objeto. Lo vemos en la línea 16.

Una vez creado un objeto de una clase, y establecidas sus propiedades, bien sea en el constructor, mediante otro método o, incluso, directamente en código, podemos leer el valor de las mismas mediante la sintaxis típica de objeto.propiedad, como vemos en las líneas 17 y 18 del código.

PRIMERAS LIMITACIONES

Aunque todavía no hemos tenido ocasión de comprobarlo, te adelanto tres limitaciones que tienen las clases en JS6 y que muchos esperamos que se solucionen en próximas revisiones del estándar.

En primer lugar, tu no puedes definir o usar variables fuera de los métodos. En otros leguajes (cómo PHP, por ejemplo, sin ir más lejos) es habitual encontrar una definición de clase con un esquema similar al siguiente:

class MiClase(){
    variable_1 = "";
    variable_2 = 0;
    variable_3 = true;
    variable_4 = "Valor inicial";
    metodo1(){
        ...
    }
}

Esta declaración previa de las variables que se usarán en el objeto, antes de empezar a declarar los métodos, es muy útil, a efectos organizativos. Cuando tu calse emplea una gran cantidad de variables, poder declararlas antes de empezar conlos métodos es realmente práctico. JavaScript lanza un error si intentas hacer algo así.

La segunda limitación, consecuencia directa de la primera, es que sólo puedes tener variables de objeto, pero no de clase. La POO en JS6 no contempla la declaración y uso de variables estáticas (aunque sí de métodos estáticos, como veremos enseguida).

También nos encontramos con otra limitación importante (y esta sí es realmente grave). No es posible declarar propiedades o métodos privados. Todos los elementos que componen un objeto son, ineludiblemente, públicos. Si estás familiarizado con la programación orientada a objetos, esto te parecerá imperdonable pero, al menos por ahora, es así.

OTRO EJEMPLO

Ampliando el código anterior, podemos tener un ejemplo como el siguiente:

El resultado en la página es el siguiente:

Nombre: Pedro
Apellidos: García Muñoz
Salario bruto: 1000
Salario neto: 813

Observa el método establecerSalario(), definido entre las líneas 15 y 20. Cómo ves, dentro de un método si se pueden declarar variables que no son propias del objeto, como hemos hecho en las líneas 17 y 18. Sin embargo, esas variables pueden emplearse, exclusivamente, cómo las hemos usado aquí, para el desarrollo del método. No son variables de clase, ni viven fuera del método en el que han sido declaradas. Y, cómo hemos comentado antes, tampoco nos es posible declarar una variable dentro de la clase fuera de los métodos.

MÉTODOS ESTÁTICOS

La sección anterior nos ha servido para hacernos una idea general del uso de clases en JS6, así como también de las limitaciones que, desafortunadamente, aún presentan. Al menos, ya que no podemos declarar variables estáticas, o variables y métodos privados, si se nos permite crear métodos estáticos (de clase). Observa el siguiente listado:

El resultado en la página es el siguiente:

PRIMER EMPLEADO
Nombre: Pedro
Apellidos: García Muñoz
Salario bruto: 1000
Salario neto: 813
=================================
SEGUNDO EMPLEADO
Nombre: Carmen
Apellidos: Martín Domínguez
Salario bruto: 1200
Salario neto: 975.6
=================================
TOTAL DE SALARIOS: 2200

Observa el método definido entre las líneas 21 y 25. Cómo ves, ha sido declarado con la palabra reservada static, lo que le convierte en un método de clase. Cómo ves, lo que hacemos es pasarle un arreglo con los objetos que tenemos definidos de la clase Personal (empleado_1 y empleado_2, en nuestro ejemplo). La forma de pasarle estos datos la conocemos ya por haberla visto, previamente, en esta serie. Dentro del método, sumamos los salarios brutos de los empleados y los devolvemos.

Observa en la línea 43 cómo invocamos a un método estático, según el esquema clase.metodo():

document.getElementById("capaDeResultados").innerHTML += "TOTAL DE SALARIOS: " + Personal.sumarSalarios(empleado_1, empleado_2) + "<br>";

Si estás familiarizado con la POO en otros lenguajes podrías estar espeando que la invocación fuese del tipo clase::metodo(), pero esta sintaxis no funciona en JavaScript.

HERENCIA

La herencia es un concepto fundamental de JavaScript. Permite que una clase, que llamaremos derivada, sea creada extendiendo otra clase, a la que llamaremos padre. De este modo, la clase derivada tendrá todas las propiedades y métodos de la clase padre, además de implementar los suyos propios. Observa el siguiente código de ejemplo:

Al cargar el documento ves en tu navegador lo siguiente:

PRIMER EMPLEADO
Nombre: Pedro
Apellidos: García Muñoz
Salario bruto: 1000
Salario neto: 813
Departamento: Mantenimiento
=================================
SEGUNDO EMPLEADO
Nombre: Carmen
Apellidos: Martín Domínguez
Salario bruto: 1200
Salario neto: 975.6
Departamento: Seguridad
=================================
TOTAL DE SALARIOS: 2200

Empecemos a ver cómo funciona. En primer lugar fíjate en cómo se ha creado la clase Empleados en la línea 28. Con la palabra reservada extends le estamos diciendo a JavaScript que esta clase que estamos creando (Empleados) deriva de Personal. Así pues, Personal es la clase padre y Empleados es la clase derivada.

Esto quiere decir que Empleados tiene todos los métodos de Personal. Eso es, exactamente, la herencia. Además, por supuesto, la clase derivada puede implementar sus propios métodos.

Un caso que requiere especial atención es el del constructor de la clase derivada. Fíjate que recibe dos parámetros (nombre y apellidos). Dentro del constructor encontramos la palabra reservada super, escrita como una función, es decir, seguida de unos paréntesis entre los que se pueden encerrar argumentos. Cuando en el constructor se invoca a super lo que está haciendo es llamar, a su vez, al constructor de la clase padre, pasándole, en este caso, los dos parámetros recibidos (nombre y apellidos). Así es realmente el constructor de la clase padre el que se ejecuta. Por supuesto, el constructor de la clase derivada podría tener más instrucciones que no figuren en el constructor de la clase padre. Supongamos que tenemos el constructor de la clase derivada (Empleados), escrito así:

constructor (nombre, apellidos, nacionalidad){
    super (nombre, apellidos);
    this.nacionalidad = nacionalidad;
}

Cuando creas un objeto de la clase Empleados le pasas tres argumentos: nombre, apellidos y nacionalidad. El constructor de Empleados, que es la clase derivada, invoca al constructor de la clase padre, pasandole el nombre y apellidos, con lo que el objeto creado ya tiene estas dos variables, porque así lo ha hecho el constructor de la clase padre. A continuación, el constructor de la clase derivada continúa ejecutándose, y crea otra variable al objeto con la nacionalidad. Este tercer argumento no ha sido pasado a super, porque el constructor de la clase padre no lo recibe ni gestiona en modo alguno.

Observa que en este último código hemos creado los dos empleados de la clase Empleados (la derivada), no de la clase Personal (la padre). Como la clase Empleados incorpora un método nuevo, al que hemos llamado establecerDepartamento(), lo hemos podido usar en los dos objetos. Si alguno de los objetos (empleado_1 o empleado_2) se hubiera instanciado de la clase padre (Personal), al tratar de usar el método establecerDepartamento() se habría producido un error, porque la clase padre no tiene este método. Es decir, la herencia significa que la clase derivada hereda todo lo que hay en la clase padre, pero la clase padre no hereda nada de la clase derivada.

La herencia en JavaScript tiene las siguientes características:

  • No se soporta la herencia múltiple. Es decir, una clase sólo puede derivar de una clase padre, no de dos o más.
  • Se soporta la sobrescritura de métodos. Un método de la clase derivada puede tener el mismo nombre e, incluso, el mismo esquema, que un método de la clase padre. Que se invoque a uno u otro dependerá, en todo caso, de la clase a la que pertenezca el objeto.
  • No se soporta la sobrecarga de métodos. Es decir, no se pueden crear, en una misma clase, dos métodos con el mismo nombre y diferente esquema, ya que sólo se reconocerá el último.

SETTERS Y GETTERS

La POO en JavaScript incorpora dos palabras reservadas específicas para hacer que un método actúe como setter o como getter. Como ya sabes, un setter es un método que permite, dado un objeto, establecer el valor de una de sus variables. Un getter, por contra, está pensado para recuperar el valor de una variable del objeto.

Sin embargo, esto es una incongruencia. La razón de ser de los setters y los getters en POO ha sido siempre proporcionar unos métodos que nos permitieran acceder a variables privadas, ya que, desde fuera de la clase, no se puede acceder directamente a estas. Dado que JavaScript 6 no contempla el uso de variables privadas, por lo que todas son públicas, no tienen ningún sentido estos métodos.

Además, si en alguna futura revisión del estándar, se incorpora el uso de variables privadas, siempre se pueden crear los setters y getters que sean necesarios.

     

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *